tests/auto/qml/qmldesigner/propertyeditortests/tst_propertyeditor
tests/auto/profilewriter/tst_profilewriter
src/tools/cplusplus/generate-ast
+src/tools/qml/qmldump/qmldump
-Qt Creator 1.3.84
+Qt Creator 2.0.80
===============
Qt Creator is a crossplatform C++ IDE for development with the Qt framework.
" </div>\n" \
" <div id=\"shortCut\">\n" \
" <ul>\n" \
- " <li class=\"shortCut-topleft-inactive\"><span><a href=\"index.html\">Qt Creator 1.3.84 </a></span></li>\n" \
+ " <li class=\"shortCut-topleft-inactive\"><span><a href=\"index.html\">Qt Creator 2.0.80 </a></span></li>\n" \
" <li class=\"shortCut-topleft-active\"><a href=\"http://qt.nokia.com/doc/\">ALL Versions" \
" </a></li>\n" \
" </ul>\n" \
\title Qt Creator Manual
- \section1 Version 2.0 (Beta)
+ \section1 Version 2.0.80
Qt Creator provides integrated tools for both application designers
and developers to create applications for multiple desktop and mobile device
\section1 Using Update Code Model
To refresh the internal information in Qt Creator pertaining to your code,
- select \gui{Tools} > \gui{C++} > \gui{Update Code Model}.
+ select \gui{Tools} > \gui{C++} > \gui{Update code model}.
\note In Qt Creator indexing updates the code automatically. Use
- \gui{Update Code Model} only as an emergency command.
+ \gui{Update code model} only as an emergency command.
*/
In the \gui{FakeVim} mode, you can run the main editor in a manner similar
to the Vim editor. To run the editor in the \gui{FakeVim} mode, select
- \gui{Edit} > \gui{Advanced} > \gui{Use Vim-style Editing} or press
+ \gui{Edit} > \gui{Advanced} > \gui{Use vim-style editing} or press
\key{Alt+V,Alt+V}.
In the \gui{FakeVim} mode, most keystrokes in the main editor will be
{Complete Guide to Symbian Signed}.
When you have your own certificate and private key, you can specify them in
- the \gui{Create SIS Package} step in your build configuration.
+ the \gui{Create sis Package} step in your build configuration.
\image qtcreator-qt4-symbian-signing.png
\gui{Add clean step} and select the type of step you want to add.
By default, custom steps are disabled. Activate custom steps by
- checking the \gui{Enable custom process step} check-box.
- \o To remove a clean step, click \gui{Remove Item}.
+ checking the \gui{Enable Custom Process Step} check-box.
+ \o To remove a clean step, click \gui{Remove clean step} and select the
+ step you want to remove.
\o To change the order of steps, click
\inlineimage qtcreator-movestep.png
.
a rebase operation while pulling.
\row
- \i \gui{Clean Repository.../Clean Project...}
+ \i \gui{Clean repository.../Clean project...}
\i Collect all files that are not under version control
with the exception of patches and project files
and show them as a checkable list in a dialog
\row
\i \gui{Stashes...}
\i Displays a dialog showing the stashes created by
- \gui{Stash Snapshot...} with options to restore,
+ \gui{Stash snapshots...} with options to restore,
display or delete them.
\row
\i \gui {Stage File for Commit}
\section2 Terminal Mode
To launch the debugger in the terminal mode, select \gui {Projects > Run Settings}
- and select the \gui {Run in terminal} check box. Then click the
+ and select the \gui {Run in Terminal} check box. Then click the
\gui {Start Debugging} button for the active project.
\section2 Attach Mode
To enable Qt's basic objects data display feature:
\list
\o Select \gui Tools > \gui {Options...} > \gui Debugger >
- \gui{Debugging Helper} and check the \gui{Use Debugging Helper}
+ \gui{Debugging Helper} and check the \gui{Use debugging helper}
checkbox.
\o The \gui{Locals and Watchers} view is reorganized to provide a
high-level view of the objects.
specified in the \c CMake project file.
Known issues for the current version can be found
- \l{Known Issues of version 1.3.84}{here}.
+ \l{Known Issues of version 2.0.80}{here}.
\section1 Adding External Libraries to a CMake Project
\o In the \gui {Configuration name} field, enter a name for
the connection.
- \o In the \gui {Device type} field, select \gui {Maemo emulator}.
+ \o In the \gui {Device type} field, select \gui {Local simulator}.
\o In the \gui {Authentication type} field, select \gui Password
for the initial connection.
\list 1
\o Select \gui Tools > \gui Options... > \gui Debugger >
\gui{Debugging Helper}.
- \o Uncheck the \gui{Use Debugging Helper} checkbox.
+ \o Uncheck the \gui{Use debugging helper} checkbox.
\endlist
*/
There are some known issues with Qt Creator.
The development team is aware of them, there is no need to report them as bugs.
- \section1 Known Issues of Version 1.3.84
+ \section1 Known Issues of Version 2.0.80
\list
\o On Windows, debugging a MinGW-built console application (with \gui{Run in terminal}
qhp.projects = QtCreator
qhp.QtCreator.file = qtcreator.qhp
-qhp.QtCreator.namespace = com.nokia.qtcreator.1384
+qhp.QtCreator.namespace = com.nokia.qtcreator.2080
qhp.QtCreator.virtualFolder = doc
qhp.QtCreator.indexTitle = Qt Creator
-qhp.QtCreator.filterAttributes = qtcreator 1.3.84
-qhp.QtCreator.customFilters.QtCreator.name = Qt Creator 1.3.84
-qhp.QtCreator.customFilters.QtCreator.filterAttributes = qtcreator 1.3.84
+qhp.QtCreator.filterAttributes = qtcreator 2.0.80
+qhp.QtCreator.customFilters.QtCreator.name = Qt Creator 2.0.80
+qhp.QtCreator.customFilters.QtCreator.filterAttributes = qtcreator 2.0.80
qhp.QtCreator.indexRoot =
qhp.QtCreator.extraFiles = \
style/style.css \
hasBlock = 'block' in __builtin__.dir(frame)
items = []
+ #warn("HAS BLOCK: %s" % hasBlock);
+ #warn("IS GOOD GDB: %s" % isGoodGdb());
if hasBlock and isGoodGdb():
#warn("IS GOOD: %s " % varList)
try:
formats = {}
watchers = ""
expandedINames = ""
+ resultVarName = ""
for arg in args.split(' '):
pos = arg.find(":") + 1
if arg.startswith("options:"):
elif arg.startswith("vars:"):
if len(arg[pos:]) > 0:
varList = arg[pos:].split(",")
+ elif arg.startswith("resultvarname:"):
+ resultVarName = arg[pos:]
elif arg.startswith("expanded:"):
expandedINames = set(arg[pos:].split(","))
elif arg.startswith("typeformats:"):
d.typeformats = typeformats
d.formats = formats
d.useFancy = useFancy
- d.passExceptions = "passexceptions" in options
+ d.passExceptions = "pe" in options
d.autoDerefPointers = "autoderef" in options
d.ns = qtNamespace()
d.expandedINames = expandedINames
#
# Locals
#
- for item in listOfLocals(varList):
+ locals = listOfLocals(varList);
+
+ # Take care of the return value of the last function call.
+ if len(resultVarName) > 0:
+ try:
+ value = parseAndEvaluate(resultVarName)
+ locals.append(Item(value, "return", resultVarName, "return"))
+ except:
+ # Don't bother. It's only supplementary information anyway.
+ pass
+
+ for item in locals:
with OutputSafer(d, "", ""):
d.anonNumber = -1
#warn("ITEM NAME %s: " % item.name)
self.putNumChild(1)
if self.isExpanded(item):
with Children(self):
- self.putItem(
- Item(item.value.dereference(), item.iname, "*", "*"))
+ with SubItem(self):
+ self.putItemHelper(Item(item.value.dereference(),
+ item.iname, "*", "*"))
+ self.putAddress(item.value)
self.putPointerValue(value.address)
elif str(type).startswith("<anon"):
d.putItemHelper(Item(value, item.iname, "value"))
+def qdump__QHostAddress(d, item):
+ data = item.value["d"]["d"].dereference()
+ d.putStringValue(data["ipString"])
+ d.putNumChild(1)
+ if d.isExpanded(item):
+ with Children(d):
+ d.putFields(Item(data, item.iname))
+
+
def qdump__QList(d, item):
d_ptr = item.value["d"]
begin = d_ptr["begin"]
# in the frontend.
# So as first approximation only do the 'isLarge' check:
isInternal = innerSize <= d_ptr.type.sizeof and d.isMovableType(innerType)
- #warn("INTERNAL: %d" % int(isInternal))
-
- p = gdb.Value(array).cast(innerType.pointer()) + begin
+ dummyType = gdb.lookup_type("void").pointer().pointer()
+ innerTypePointer = innerType.pointer()
+ p = gdb.Value(array).cast(dummyType) + begin
if innerTypeIsPointer:
inner = innerType.target()
else:
with Children(d, [size, 2000], inner):
for i in d.childRange():
if isInternal:
- d.putItem(Item(p.dereference(), item.iname, i))
+ pp = p.cast(innerTypePointer).dereference();
+ d.putItem(Item(pp, item.iname, i))
else:
- pp = p.cast(innerType.pointer().pointer()).dereference()
- d.putItem(Item(pp.dereference(), item.iname, i))
+ pp = p.cast(innerTypePointer.pointer()).dereference()
+ d.putItem(Item(pp, item.iname, i))
p += 1
def qdump__QString(d, item):
d.putStringValue(item.value)
d.putNumChild(0)
+ d.putField("typeformats", "Normal,Displayed");
+ format = d.itemFormat(item)
+ if format == 0:
+ d.putDisplay(StopDisplay)
+ elif format == 1:
+ d.putField("editformat", 2)
+ str = encodeString(item.value)
+ d.putField("editvalue", str)
def qdump__QStringList(d, item):
def qdumpHelper__QVariant(d, value):
- #warn("VARIANT TYPE: %s : " % variantType)
data = value["d"]["data"]
variantType = int(value["d"]["type"])
+ #warn("VARIANT TYPE: %s : " % variantType)
val = None
inner = ""
innert = ""
def qdump__QVariant(d, item):
val, inner, innert = qdumpHelper__QVariant(d, item.value)
+ #warn("VARIANT DATA: '%s' '%s' '%s': " % (val, inner, innert))
- if len(inner):
- # Build-in types.
- #d.putValue("(%s)" % innert)
- d.putType("%sQVariant (%s)" % (d.ns, innert))
- d.putNumChild(1)
- if d.isExpanded(item):
- with Children(d):
- d.putItem(Item(val, item.iname, "data", "data"))
+ if len(inner):
+ innerType = gdb.lookup_type(inner)
+ # FIXME: Why "shared"?
+ if innerType.sizeof > item.value["d"]["data"].type.sizeof:
+ v = item.value["d"]["data"]["shared"]["ptr"] \
+ .cast(innerType.pointer()).dereference()
+ else:
+ v = item.value["d"]["data"].cast(innerType)
+ d.putItemHelper(Item(v, item.iname))
+ d.putType("%sQVariant (%s)" % (d.ns, innert), d.currentTypePriority + 1)
else:
# User types.
d_member = item.value["d"]
#######################################################################
if False:
+
+ # FIXME: Make that work
+ def qdump__Color(d, item):
+ v = item.value
+ d.putValue("(%s, %s, %s; %s)" % (v["r"], v["g"], v["b"], v["a"]))
+ if d.isExpanded(item):
+ with Children(d):
+ d.putItem(Item(v["r"], item.iname, "0", "r"))
+ d.putItem(Item(v["g"], item.iname, "1", "g"))
+ d.putItem(Item(v["b"], item.iname, "2", "b"))
+ d.putItem(Item(v["a"], item.iname, "3", "a"))
+
+ def qdump__Color_(d, item):
+ v = item.value
+ d.putValue("(%s, %s, %s; %s)" % (v["r"], v["g"], v["b"], v["a"]))
+ if d.isExpanded(item):
+ with Children(d):
+ with SubItem(d):
+ d.putField("iname", item.iname + ".0")
+ d.putItemHelper(Item(v["r"], item.iname, "0", "r"))
+ with SubItem(d):
+ d.putField("iname", item.iname + ".1")
+ d.putItemHelper(Item(v["g"], item.iname, "1", "g"))
+ with SubItem(d):
+ d.putField("iname", item.iname + ".2")
+ d.putItemHelper(Item(v["b"], item.iname, "2", "b"))
+ with SubItem(d):
+ d.putField("iname", item.iname + ".3")
+ d.putItemHelper(Item(v["a"], item.iname, "3", "a"))
+
+
def qdump__Function(d, item):
min = item.value["min"]
max = item.value["max"]
individualformats = None,
watchers = None):
self.options = options
- self.expanded = expanded
+ self.expandedINames = expanded
self.typeformats = typeformats
self.individualformats = individualformats
self.watchers = watchers
- self.doit()
+ if self.options == "listmodules":
+ self.handleListModules()
+ elif self.options == "listsymbols":
+ self.handleListSymbols(expanded)
+ else:
+ self.handleListVars()
def put(self, value):
sys.stdout.write(value)
t = str(type)
if t.startswith("<type '") and t.endswith("'>"):
t = t[7:-2]
+ if t.startswith("<class '") and t.endswith("'>"):
+ t = t[8:-2]
return t
def putType(self, type, priority = 0):
def putName(self, name):
self.put('name="%s",' % name)
- def isExpanded(self, item):
- #warn("IS EXPANDED: %s in %s" % (item.iname, self.expandedINames))
- if item.iname is None:
- raise "Illegal iname 'None'"
- if item.iname.startswith("None"):
- raise "Illegal iname '%s'" % item.iname
- #warn(" --> %s" % (item.iname in self.expandedINames))
- return item.iname in self.expandedINames
+ def isExpanded(self, iname):
+ #self.warn("IS EXPANDED: %s in %s" % (iname, self.expandedINames))
+ if iname.startswith("None"):
+ raise "Illegal iname '%s'" % iname
+ #self.warn(" --> %s" % (iname in self.expandedINames))
+ return iname in self.expandedINames
def isExpandedIName(self, iname):
return iname in self.expandedINames
tt = self.cleanType(t)
if tt == "module" or tt == "function":
return
+ if str(value).startswith("<class '"):
+ return
+ # FIXME: Should we?
+ if str(value).startswith("<enum-item "):
+ return
self.put("{")
self.putField("iname", iname)
self.putName(name)
self.putType(tt)
- if tt == "list" or tt == "tuple":
+ if tt == "NoneType":
+ self.putValue("None")
+ self.putNumChild(0)
+ elif tt == "list" or tt == "tuple":
self.putItemCount(len(value))
#self.putValue(value)
self.put("children=[")
self.put("{")
self.putType(" ")
self.putValue("%s: %s" % (k, v))
- self.put("children=[")
- self.dumpValue(k, "key", "%s.%d.k" % (iname, i))
- self.dumpValue(v, "value", "%s.%d.v" % (iname, i))
- self.put("]},")
+ if self.isExpanded(iname):
+ self.put("children=[")
+ self.dumpValue(k, "key", "%s.%d.k" % (iname, i))
+ self.dumpValue(v, "value", "%s.%d.v" % (iname, i))
+ self.put("]},")
i += 1
self.put("]")
- elif tt == "module" or tt == "function":
+ elif tt == "class":
+ pass
+ elif tt == "module":
+ pass
+ elif tt == "function":
pass
+ elif str(value).startswith("<enum-item "):
+ # FIXME: Having enums always shown like this is not nice.
+ self.putValue(str(value)[11:-1])
+ self.putNumChild(0)
else:
- self.putValue(value)
+ v = str(value)
+ p = v.find(" object at ")
+ if p > 1:
+ v = "@" + v[p + 11:-1]
+ self.putValue(v)
+ if self.isExpanded(iname):
+ self.put("children=[")
+ for child in dir(value):
+ if child == "__dict__":
+ continue
+ if child == "__doc__":
+ continue
+ if child == "__module__":
+ continue
+ attr = getattr(value, child)
+ if callable(attr):
+ continue
+ try:
+ self.dumpValue(attr, child, "%s.%s" % (iname, child))
+ except:
+ pass
+ self.put("],")
self.put("},")
def warn(self, msg):
self.putField("warning", msg)
- def doit(self):
+ def handleListVars(self):
# Trigger error to get a backtrace.
frame = None
#self.warn("frame: %s" % frame)
n = n + 1
sys.stdout.flush()
+
+ def handleListModules(self):
+ self.put("modules=[");
+ for name in sys.modules:
+ self.put("{")
+ self.putName(name)
+ self.putValue(sys.modules[name])
+ self.put("},")
+ self.put("]")
+ sys.stdout.flush()
+
+ def handleListSymbols(self, module):
+ #self.put("symbols=%s" % dir(sys.modules[module]))
+ self.put("symbols=[");
+ for name in sys.modules:
+ self.put("{")
+ self.putName(name)
+ #self.putValue(sys.modules[name])
+ self.put("},")
+ self.put("]")
+ sys.stdout.flush()
--- /dev/null
+Place syntax definitions files inside this directory.
+Files can be downloaded at http://kate-editor.org/downloads/syntax_highlighting?kateversion=3.2
+Instructions on how to write your own definitions can be found at http://kate-editor.org/article/writing_a_kate_highlighting_xml_file
styles \
gdbmacros \
qmldesigner \
- qml-type-descriptions
+ qml-type-descriptions \
+ generic-highlighter
!isEmpty(copydata) {
--- /dev/null
+<plugin name="%PluginName%" version="0.0.1" compatVersion="0.0.1">
+ <vendor>%VendorName%</vendor>
+ <copyright>%Copyright%</copyright>
+ <license>%License%</license>
+ <description>%Description%</description>
+ <url>%URL%</url>
+ <dependencyList>
+ <dependency name="Core" version="2.0.80"/>
+ </dependencyList>
+</plugin>
--- /dev/null
+#include "%PluginName:l%.%CppHeaderSuffix%"
+
+#include <QtPlugin>
+
+#include <coreplugin/icore.h>
+#include <coreplugin/actionmanager/actionmanager.h>
+#include <coreplugin/actionmanager/command.h>
+#include <coreplugin/actionmanager/actioncontainer.h>
+#include <coreplugin/coreconstants.h>
+
+#include <QtPlugin>
+#include <QAction>
+#include <QMessageBox>
+#include <QMainWindow>
+#include <QMenu>
+
+namespace {
+ const char * const ACTION_ID = "%PluginName%.Action";
+ const char * const MENU_ID = "%PluginName%.Menu";
+}
+
+using namespace %PluginName%::Internal;
+
+%PluginName%Impl::%PluginName%Impl()
+{
+ // Create your members
+}
+
+%PluginName%Impl::~%PluginName%Impl()
+{
+ // Unregister objects from the plugin manager's object pool
+ // Delete members
+}
+
+bool %PluginName%Impl::initialize(const QStringList &arguments, QString *errorString)
+{
+ // Register objects in the plugin manager's object pool
+ // Load settings
+ // Add actions to menus
+ // connect to other plugins' signals
+ // "In the initialize method, a plugin can be sure that the plugins it
+ // depends on have initialized their members."
+
+ Q_UNUSED(arguments)
+ Q_UNUSED(errorString)
+ Core::ActionManager *am = Core::ICore::instance()->actionManager();
+
+ QAction *action = new QAction(tr("%PluginName% action"), this);
+ Core::Command *cmd = am->registerAction(action, QLatin1String(ACTION_ID),
+ QList<int>() << Core::Constants::C_GLOBAL_ID);
+ cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+Meta+A")));
+ connect(action, SIGNAL(triggered()), this, SLOT(triggerAction()));
+
+ Core::ActionContainer *menu = am->createMenu(MENU_ID);
+ menu->menu()->setTitle(tr("%PluginName%"));
+ menu->addAction(cmd);
+ am->actionContainer(Core::Constants::M_TOOLS)->addMenu(menu);
+
+ return true;
+}
+
+void %PluginName%Impl::extensionsInitialized()
+{
+ // Retrieve objects from the plugin manager's object pool
+ // "In the extensionsInitialized method, a plugin can be sure that all
+ // plugins that depend on it are completely initialized."
+}
+
+void %PluginName%Impl::aboutToShutdown()
+{
+ // Save settings
+ // Disconnect from signals that are not needed during shutdown
+}
+
+void %PluginName%Impl::triggerAction()
+{
+ QMessageBox::information(Core::ICore::instance()->mainWindow(),
+ tr("Action triggered"),
+ tr("This is an action from %PluginName%."));
+}
+
+Q_EXPORT_PLUGIN2(%PluginName%, %PluginName%Impl)
--- /dev/null
+#ifndef %PluginName:u%_%CppHeaderSuffix:u%
+#define %PluginName:u%_%CppHeaderSuffix:u%
+
+#include "%PluginName:l%_global.%CppHeaderSuffix%"
+
+#include <extensionsystem/iplugin.h>
+
+namespace %PluginName% {
+namespace Internal {
+
+class %PluginName%Impl : public ExtensionSystem::IPlugin
+{
+ Q_OBJECT
+
+public:
+ %PluginName%Impl();
+ ~%PluginName%Impl();
+
+ bool initialize(const QStringList &arguments, QString *errorString);
+ void extensionsInitialized();
+ void aboutToShutdown();
+
+private slots:
+ void triggerAction();
+};
+
+} // namespace Internal
+} // namespace %PluginName%
+
+#endif // %PluginName:u%_%CppHeaderSuffix:u%
--- /dev/null
+TARGET = %PluginName%
+TEMPLATE = lib
+
+DEFINES += %PluginName:u%_LIBRARY
+
+# %PluginName% files
+
+SOURCES += %PluginName:l%.cpp
+
+HEADERS += %PluginName:l%.h\
+ %PluginName:l%_global.h
+
+OTHER_FILES = %PluginName%.pluginspec
+
+
+# Qt Creator linking
+
+## set the QTC_SOURCE environment variable to override the setting here
+QTCREATOR_SOURCES = $$(QTC_SOURCE)
+isEmpty(QTCREATOR_SOURCES):QTCREATOR_SOURCES=%QtCreatorSources%
+
+## set the QTC_BUILD environment variable to override the setting here
+IDE_BUILD_TREE = $$(QTC_BUILD)
+isEmpty(IDE_BUILD_TREE):IDE_BUILD_TREE=%QtCreatorBuild%
+
+PROVIDER = %VendorName%
+
+include($$QTCREATOR_SOURCES/src/qtcreatorplugin.pri)
+include($$QTCREATOR_SOURCES/src/plugins/coreplugin/coreplugin.pri)
+
+LIBS += -L$$IDE_PLUGIN_PATH/Nokia
--- /dev/null
+#ifndef %PluginName:u%_GLOBAL_%CppHeaderSuffix:u%
+#define %PluginName:u%_GLOBAL_%CppHeaderSuffix:u%
+
+#include <QtCore/qglobal.h>
+
+#if defined(%PluginName:u%_LIBRARY)
+# define %PluginName:u%SHARED_EXPORT Q_DECL_EXPORT
+#else
+# define %PluginName:u%SHARED_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // %PluginName:u%_GLOBAL_H
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**************************************************************************
+**
+** 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.
+**
+**************************************************************************/
+
+Custom project wizard configuration example file. Note that by convention,
+the project file goes last.
+The "class" and "firstpage" attributes specify that it is a Qt 4 wizard and
+leave room for the Qt 4 target page.
+-->
+<wizard version="1" kind="project"
+ class="qt4project" firstpage="10"
+ id="R.QtCreatorPlugin" category="I.Projects">
+ <icon>qtcreator_logo_24.png</icon>
+ <description>Creates a custom Qt Creator plugin.</description>
+ <displayname>Qt Creator plugin</displayname>;
+ <displaycategory>Other Project</displaycategory>
+ <files>
+ <file source="myplugin.pro" target="%PluginName:l%.pro" openproject="true"/>
+ <file source="MyPlugin.pluginspec" target="%PluginName%.pluginspec" openeditor="true"/>
+ <file source="myplugin.h" target="%PluginName:l%.%CppHeaderSuffix%" openeditor="true"/>
+ <file source="myplugin.cpp" target="%PluginName:l%.%CppSourceSuffix%" openeditor="true"/>
+ <file source="myplugin_global.h" target="%PluginName:l%_global.%CppHeaderSuffix%" openeditor="true"/>
+ </files>
+ <!-- Create a 2nd wizard page with parameters -->
+ <fieldpagetitle>Plugin Information</fieldpagetitle>
+ <fieldpagetitle xml:lang="de">Plugininformationen</fieldpagetitle>
+ <fields>
+ <field mandatory="true" name="PluginName">
+ <fieldcontrol class="QLineEdit" validator="^[a-zA-Z0-9_]+$"
+ defaulttext="MyPlugin" />
+ <fielddescription>Plugin name:</fielddescription>
+ <fielddescription xml:lang="de">Name des Plugins:</fielddescription>
+ </field>
+ <field mandatory="true" name="VendorName">
+ <fieldcontrol class="QLineEdit" validator="^[a-zA-Z0-9_]+$"
+ defaulttext="MyCompany" />
+ <fielddescription>Vendor name:</fielddescription>
+ <fielddescription xml:lang="de">Name des Anbieters:</fielddescription>
+ </field>
+ <field name="Copyright">
+ <fieldcontrol class="QLineEdit"
+ defaulttext="(C) MyCompany" />
+ <fielddescription>Copyright:</fielddescription>
+ <fielddescription xml:lang="de">Copyright:</fielddescription>
+ </field>
+ <field name="License">
+ <fieldcontrol class="QTextEdit"
+ defaulttext="Put your license text here" />
+ <fielddescription>License:</fielddescription>
+ <fielddescription xml:lang="de">Lizenz:</fielddescription>
+ </field>
+ <field name="Description">
+ <fieldcontrol class="QTextEdit"
+ defaulttext="Put a short description of your plugin here"/>
+ <fielddescription>Description:</fielddescription>
+ <fielddescription xml:lang="de">Beschreibung:</fielddescription>
+ </field>
+ <field name="URL">
+ <fieldcontrol class="QLineEdit"
+ defaulttext="http://www.mycompany.com" />
+ <fielddescription>Url:</fielddescription>
+ <fielddescription xml:lang="de">Url:</fielddescription>
+ </field>
+ <field mandatory="true" name="QtCreatorSources">
+ <fieldcontrol class="Utils::PathChooser"
+ defaulttext="" />
+ <fielddescription>Qt Creator sources:</fielddescription>
+ <fielddescription xml:lang="de">Qt Creator Quellen:</fielddescription>
+ </field>
+ <field mandatory="true" name="QtCreatorBuild">
+ <fieldcontrol class="Utils::PathChooser"
+ defaulttext="" />
+ <fielddescription>Qt Creator build:</fielddescription>
+ <fielddescription xml:lang="de">Qt Creator build:</fielddescription>
+ </field>
+ </fields>
+</wizard>
<key>CFBundleIdentifier</key>
<string>com.nokia.qtcreator</string>
<key>CFBundleVersion</key>
- <string>1.3.84</string>
+ <string>2.0.80</string>
<key>CFBundleShortVersionString</key>
- <string>1.3.84</string>
+ <string>2.0.80</string>
</dict>
</plist>
return 0;
}
-int ne7ssh::connectWithPassword (const char *host, const int port, const char* username, const char* password, bool shell, const int timeout)
+int ne7ssh::connectWithPassword (const char *host, const int port,
+ const char* username, const char* password, bool shell, const int timeout,
+ void (*callbackFunc)(void *), void *callbackArg)
{
int channel;
uint32 currentRecord, z;
uint32 channelID;
- ne7ssh_connection* con = new ne7ssh_connection ();
+ ne7ssh_connection* con = new ne7ssh_connection (callbackFunc, callbackArg);
if (!lock()) return -1;
if (!conCount) connections = (ne7ssh_connection**) malloc (sizeof (ne7ssh_connection*));
return channel;
}
-int ne7ssh::connectWithKey (const char* host, const int port, const char* username, const char* privKeyFileName, bool shell, const int timeout)
+int ne7ssh::connectWithKey (const char* host, const int port,
+ const char* username, const char* privKeyFileName, bool shell,
+ const int timeout, void (*callbackFunc)(void *), void *callbackArg)
{
int channel;
uint32 currentRecord, z;
uint32 channelID;
- ne7ssh_connection* con = new ne7ssh_connection ();
+ ne7ssh_connection* con = new ne7ssh_connection (callbackFunc, callbackArg);
if (!lock()) return -1;
if (!conCount) connections = (ne7ssh_connection**) malloc (sizeof (ne7ssh_connection*) * (conCount + 1));
else connections = (ne7ssh_connection**) realloc (connections, sizeof (ne7ssh_connection*) * (conCount + 1));
* @param timeout Timeout for the connection procedure, in seconds.
* @return Returns newly assigned channel ID, or -1 if connection failed.
*/
- int connectWithPassword (const char* host, const int port, const char* username, const char* password, bool shell = true, const int timeout = 0);
+ int connectWithPassword (const char* host, const int port, const char* username,
+ const char* password, bool shell = true, const int timeout = 0,
+ void (*callbackFunc)(void *) = 0, void *callbackArg = 0);
/**
* Connect to remote host using SSH2 protocol, with publickey authentication.
* @param timeout Timeout for the connection procedure, in seconds.
* @return Returns newly assigned channel ID, or -1 if connection failed.
*/
- int connectWithKey (const char* host, const int port, const char* username, const char* privKeyFileName, bool shell = true, const int timeout = 0);
+ int connectWithKey (const char* host, const int port, const char* username,
+ const char* privKeyFileName, bool shell = true, const int timeout = 0,
+ void (*callbackFunc)(void *) = 0, void *callbackArg = 0);
/**
* Retrieves a pointer to all current connections.
using namespace Botan;
-ne7ssh_connection::ne7ssh_connection() : sock (-1), thisChannel(0), sftp(0), connected(false), cmdRunning(false), cmdClosed(false)
+ne7ssh_connection::ne7ssh_connection(void (*callbackFunc)(void *),
+ void *callbackArg)
+ : sock (-1), thisChannel(0), sftp(0), connected(false), cmdRunning(false),
+ cmdClosed(false), callbackFunc(callbackFunc), callbackArg(callbackArg)
{
session = new ne7ssh_session();
crypto = new ne7ssh_crypt(session);
void ne7ssh_connection::handleData ()
{
channel->receive();
+ if (callbackFunc && getReceived().size() > 0)
+ callbackFunc(callbackArg);
}
void ne7ssh_connection::sendData (const char* data)
bool cmdRunning;
bool cmdClosed;
+ void (*callbackFunc)(void *);
+ void *callbackArg;
/**
* Checks if remote side is returning a correctly formated SSH version string, and makes sure that version 2 of SSH protocol is supported by the remote side.
/**
* ne7ssh_connection class constructor.
*/
- ne7ssh_connection();
+ ne7ssh_connection(void (*callbackFunc)(void *) = 0, void *callbackArg = 0);
/**
* ne7ssh_connection class destructor.
addType(e->name());
} else if (ForwardClassDeclaration *fwd = member->asForwardClassDeclaration()) {
addType(fwd->name());
+ } else if (NamespaceAlias *alias = member->asNamespaceAlias()) {
+ addType(alias->name());
} else if (Declaration *decl = member->asDeclaration()) {
if (decl->isTypedef())
addType(decl->name());
return false;
}
+bool CheckUndefinedSymbols::visit(NamespaceAliasDefinitionAST *ast)
+{
+ if (const Identifier *id = identifier(ast->namespace_name_token))
+ _types.insert(QByteArray(id->chars(), id->size()));
+
+ return true;
+}
+
bool CheckUndefinedSymbols::visit(NamedTypeSpecifierAST *ast)
{
if (ast->name) {
virtual bool visit(QualifiedNameAST *ast);
virtual bool visit(CastExpressionAST *ast);
virtual bool visit(SizeofExpressionAST *ast);
+ virtual bool visit(NamespaceAliasDefinitionAST *ast);
virtual bool visit(ObjCClassDeclarationAST *ast);
virtual bool visit(ObjCProtocolRefsAST *ast);
**************************************************************************/
#include "CppDocument.h"
-#include "CppBindings.h"
#include "FastPreprocessor.h"
+#include "LookupContext.h"
+#include "Overview.h"
#include <Control.h>
#include <TranslationUnit.h>
#include <Semantic.h>
#include <Literals.h>
#include <Symbols.h>
+#include <Names.h>
#include <AST.h>
#include <Scope.h>
+#include <SymbolVisitor.h>
#include <QtCore/QByteArray>
#include <QtCore/QBitArray>
namespace {
+class FindScopeAt: protected SymbolVisitor
+{
+ TranslationUnit *_unit;
+ unsigned _line;
+ unsigned _column;
+ Scope *_scope;
+
+public:
+ FindScopeAt(TranslationUnit *unit, unsigned line, unsigned column)
+ : _unit(unit), _line(line), _column(column), _scope(0) {}
+
+ Scope *operator()(Symbol *symbol)
+ {
+ accept(symbol);
+ return _scope;
+ }
+
+protected:
+ bool process(ScopedSymbol *symbol)
+ {
+ if (! _scope) {
+ Scope *scope = symbol->members();
+
+ for (unsigned i = 0; i < scope->symbolCount(); ++i) {
+ accept(scope->symbolAt(i));
+
+ if (_scope)
+ return false;
+ }
+
+ unsigned startLine, startColumn;
+ _unit->getPosition(symbol->startOffset(), &startLine, &startColumn);
+
+ if (_line > startLine || (_line == startLine && _column >= startColumn)) {
+ unsigned endLine, endColumn;
+ _unit->getPosition(symbol->endOffset(), &endLine, &endColumn);
+
+ if (_line < endLine || (_line == endLine && _column < endColumn))
+ _scope = scope;
+ }
+ }
+
+ return false;
+ }
+
+ using SymbolVisitor::visit;
+
+ virtual bool preVisit(Symbol *)
+ { return ! _scope; }
+
+ virtual bool visit(UsingNamespaceDirective *) { return false; }
+ virtual bool visit(UsingDeclaration *) { return false; }
+ virtual bool visit(NamespaceAlias *) { return false; }
+ virtual bool visit(Declaration *) { return false; }
+ virtual bool visit(Argument *) { return false; }
+ virtual bool visit(TypenameArgument *) { return false; }
+ virtual bool visit(BaseClass *) { return false; }
+ virtual bool visit(ForwardClassDeclaration *) { return false; }
+
+ virtual bool visit(Enum *symbol)
+ { return process(symbol); }
+
+ virtual bool visit(Function *symbol)
+ { return process(symbol); }
+
+ virtual bool visit(Namespace *symbol)
+ { return process(symbol); }
+
+ virtual bool visit(Class *symbol)
+ { return process(symbol); }
+
+ virtual bool visit(Block *symbol)
+ { return process(symbol); }
+
+ // Objective-C
+ virtual bool visit(ObjCBaseClass *) { return false; }
+ virtual bool visit(ObjCBaseProtocol *) { return false; }
+ virtual bool visit(ObjCForwardClassDeclaration *) { return false; }
+ virtual bool visit(ObjCForwardProtocolDeclaration *) { return false; }
+ virtual bool visit(ObjCPropertyDeclaration *) { return false; }
+
+ virtual bool visit(ObjCClass *symbol)
+ { return process(symbol); }
+
+ virtual bool visit(ObjCProtocol *symbol)
+ { return process(symbol); }
+
+ virtual bool visit(ObjCMethod *symbol)
+ { return process(symbol); }
+};
+
class DocumentDiagnosticClient : public DiagnosticClient
{
enum { MAX_MESSAGE_COUNT = 10 };
localFileName.size());
_translationUnit = new TranslationUnit(_control, fileId);
_translationUnit->setQtMocRunEnabled(true);
- _translationUnit->setCxxOxEnabled(false);
+ _translationUnit->setCxxOxEnabled(true);
_translationUnit->setObjCEnabled(true);
(void) _control->switchTranslationUnit(_translationUnit);
}
_globalNamespace = globalNamespace;
}
-Symbol *Document::findSymbolAt(unsigned line, unsigned column) const
+Scope *Document::scopeAt(unsigned line, unsigned column)
{
- return findSymbolAt(line, column, globalSymbols());
+ FindScopeAt findScopeAt(_translationUnit, line, column);
+ if (Scope *scope = findScopeAt(_globalNamespace))
+ return scope;
+ return globalSymbols();
}
-Symbol *Document::findSymbolAt(unsigned line, unsigned column, Scope *scope) const
+Symbol *Document::lastVisibleSymbolAt(unsigned line, unsigned column) const
+{
+ return lastVisibleSymbolAt(line, column, globalSymbols());
+}
+
+Symbol *Document::lastVisibleSymbolAt(unsigned line, unsigned column, Scope *scope) const
{
Symbol *previousSymbol = 0;
if (previousSymbol) {
if (ScopedSymbol *scoped = previousSymbol->asScopedSymbol()) {
- if (Symbol *member = findSymbolAt(line, column, scoped->members()))
+ if (Symbol *member = lastVisibleSymbolAt(line, column, scoped->members()))
return member;
}
}
return newDoc;
}
-QSharedPointer<NamespaceBinding> Snapshot::globalNamespaceBinding(Document::Ptr doc) const
-{
- return CPlusPlus::bind(doc, *this);
-}
-
Document::Ptr Snapshot::document(const QString &fileName) const
{
return _documents.value(fileName);
}
}
}
+
+namespace {
+class FindMatchingDefinition: public SymbolVisitor
+{
+ Symbol *_declaration;
+ QList<Function *> _result;
+
+public:
+ FindMatchingDefinition(Symbol *declaration)
+ : _declaration(declaration) {}
+
+ QList<Function *> result() const { return _result; }
+
+ using SymbolVisitor::visit;
+
+ virtual bool visit(Function *fun)
+ {
+ if (_declaration->identifier()->isEqualTo(fun->identifier()))
+ _result.append(fun);
+
+ return false;
+ }
+
+ virtual bool visit(Block *)
+ {
+ return false;
+ }
+};
+} // end of anonymous namespace
+
+Symbol *Snapshot::findMatchingDefinition(Symbol *symbol) const
+{
+ if (! symbol->identifier())
+ return 0;
+
+ Document::Ptr thisDocument = document(QString::fromUtf8(symbol->fileName(), symbol->fileNameLength()));
+ if (! thisDocument) {
+ qWarning() << "undefined document:" << symbol->fileName();
+ return 0;
+ }
+
+ LookupContext thisContext(thisDocument, *this);
+ const QList<Symbol *> declarationCandidates = thisContext.lookup(symbol->name(), symbol->scope());
+ if (declarationCandidates.isEmpty()) {
+ qWarning() << "unresolved declaration:" << symbol->fileName() << symbol->line() << symbol->column();
+ return 0;
+ }
+
+ Symbol *declaration = declarationCandidates.first();
+ Function *declarationTy = declaration->type()->asFunctionType();
+ if (! declarationTy) {
+ qWarning() << "not a function:" << declaration->fileName() << declaration->line() << declaration->column();
+ return 0;
+ }
+
+ foreach (Document::Ptr doc, *this) {
+ if (! doc->control()->findIdentifier(declaration->identifier()->chars(),
+ declaration->identifier()->size()))
+ continue;
+
+ FindMatchingDefinition candidates(declaration);
+ candidates.accept(doc->globalNamespace());
+
+ const QList<Function *> result = candidates.result();
+ if (! result.isEmpty()) {
+ LookupContext context(doc, *this);
+
+ QList<Function *> viableFunctions;
+
+ ClassOrNamespace *enclosingType = context.lookupType(declaration);
+ if (! enclosingType)
+ continue; // nothing to do
+
+ foreach (Function *fun, result) {
+ const QList<Symbol *> declarations = context.lookup(fun->name(), fun->scope());
+ if (declarations.isEmpty())
+ continue;
+
+ else if (enclosingType == context.lookupType(declarations.first()))
+ viableFunctions.append(fun);
+ }
+
+ if (viableFunctions.isEmpty())
+ continue;
+
+ else if (viableFunctions.length() == 1)
+ return viableFunctions.first();
+
+ Function *best = 0;
+
+ foreach (Function *fun, viableFunctions) {
+ if (fun->identity()->isEqualTo(declaration->identity()))
+ continue;
+
+ else if (fun->argumentCount() == declarationTy->argumentCount()) {
+ if (! best)
+ best = fun;
+
+ unsigned argc = 0;
+ for (; argc < declarationTy->argumentCount(); ++argc) {
+ Symbol *arg = fun->argumentAt(argc);
+ Symbol *otherArg = declarationTy->argumentAt(argc);
+ if (! arg->type().isEqualTo(otherArg->type()))
+ break;
+ }
+
+ if (argc == declarationTy->argumentCount())
+ best = fun;
+ }
+ }
+
+ if (! best)
+ best = viableFunctions.first();
+
+ return best;
+ }
+ }
+
+ return 0;
+}
class Macro;
class MacroArgumentReference;
-class NamespaceBinding;
class CPLUSPLUS_EXPORT Document
{
QList<Macro> definedMacros() const
{ return _definedMacros; }
- Symbol *findSymbolAt(unsigned line, unsigned column) const;
+ Symbol *lastVisibleSymbolAt(unsigned line, unsigned column) const;
+ Scope *scopeAt(unsigned line, unsigned column);
QByteArray source() const;
void setSource(const QByteArray &source);
const UndefinedMacroUse *findUndefinedMacroUseAt(unsigned offset) const;
private:
- Symbol *findSymbolAt(unsigned line, unsigned column, Scope *scope) const;
+ Symbol *lastVisibleSymbolAt(unsigned line, unsigned column, Scope *scope) const;
private:
QString _fileName;
Document::Ptr documentFromSource(const QByteArray &preprocessedCode,
const QString &fileName) const;
- QSharedPointer<NamespaceBinding> globalNamespaceBinding(Document::Ptr doc) const;
+ Symbol *findMatchingDefinition(Symbol *symbol) const;
private:
void simplified_helper(Document::Ptr doc, Snapshot *snapshot) const;
**
**************************************************************************/
-#include "GenTemplateInstance.h"
+#include "DeprecatedGenTemplateInstance.h"
#include "Overview.h"
#include <Control.h>
class ApplySubstitution
{
public:
- ApplySubstitution(const LookupContext &context, Symbol *symbol, const GenTemplateInstance::Substitution &substitution);
+ ApplySubstitution(Control *control, Symbol *symbol, const DeprecatedGenTemplateInstance::Substitution &substitution);
~ApplySubstitution();
- Control *control() const { return context.control(); }
+ inline Control *control() const { return _control; }
FullySpecifiedType apply(const Name *name);
FullySpecifiedType apply(const FullySpecifiedType &type);
};
public: // attributes
- LookupContext context;
+ Control *_control;
Symbol *symbol;
- GenTemplateInstance::Substitution substitution;
+ DeprecatedGenTemplateInstance::Substitution substitution;
ApplyToType applyToType;
ApplyToName applyToName;
};
-ApplySubstitution::ApplySubstitution(const LookupContext &context, Symbol *symbol,
- const GenTemplateInstance::Substitution &substitution)
- : context(context), symbol(symbol),
+ApplySubstitution::ApplySubstitution(Control *control, Symbol *symbol,
+ const DeprecatedGenTemplateInstance::Substitution &substitution)
+ : _control(control), symbol(symbol),
substitution(substitution),
applyToType(this), applyToName(this)
{ }
} // end of anonymous namespace
-GenTemplateInstance::GenTemplateInstance(const LookupContext &context, const Substitution &substitution)
+DeprecatedGenTemplateInstance::DeprecatedGenTemplateInstance(Control *control, const Substitution &substitution)
: _symbol(0),
- _context(context),
+ _control(control),
_substitution(substitution)
{ }
-FullySpecifiedType GenTemplateInstance::operator()(Symbol *symbol)
+FullySpecifiedType DeprecatedGenTemplateInstance::gen(Symbol *symbol)
{
- ApplySubstitution o(_context, symbol, _substitution);
+ ApplySubstitution o(_control, symbol, _substitution);
return o.apply(symbol->type());
}
-Control *GenTemplateInstance::control() const
-{ return _context.control(); }
+FullySpecifiedType DeprecatedGenTemplateInstance::instantiate(const Name *className, Symbol *candidate, Control *control)
+{
+ if (className) {
+ if (const TemplateNameId *templId = className->asTemplateNameId()) {
+ if (Class *klass = candidate->enclosingSymbol()->asClass()) {
+ DeprecatedGenTemplateInstance::Substitution subst;
+
+ for (unsigned i = 0; i < templId->templateArgumentCount(); ++i) {
+ FullySpecifiedType templArgTy = templId->templateArgumentAt(i);
+
+ if (i < klass->templateParameterCount()) {
+ const Name *templArgName = klass->templateParameterAt(i)->name();
+
+ if (templArgName && templArgName->identifier()) {
+ const Identifier *templArgId = templArgName->identifier();
+ subst.append(qMakePair(templArgId, templArgTy));
+ }
+ }
+ }
+
+ DeprecatedGenTemplateInstance inst(control, subst);
+ return inst.gen(candidate);
+ }
+ }
+ }
+
+ return candidate->type();
+}
**
**************************************************************************/
-#ifndef GENTEMPLATEINSTANCE_H
-#define GENTEMPLATEINSTANCE_H
+#ifndef CPLUSPLUS_DEPRECATEDGENTEMPLATEINSTANCE_H
+#define CPLUSPLUS_DEPRECATEDGENTEMPLATEINSTANCE_H
#include <TypeVisitor.h>
#include <NameVisitor.h>
#include <FullySpecifiedType.h>
-#include "LookupContext.h"
+#include "DeprecatedLookupContext.h"
#include <QtCore/QList>
#include <QtCore/QPair>
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT GenTemplateInstance
+class CPLUSPLUS_EXPORT DeprecatedGenTemplateInstance
{
public:
typedef QList< QPair<const Identifier *, FullySpecifiedType> > Substitution;
public:
- GenTemplateInstance(const LookupContext &context, const Substitution &substitution);
+ static FullySpecifiedType instantiate(const Name *className, Symbol *candidate, Control *control);
- FullySpecifiedType operator()(Symbol *symbol);
-
- Control *control() const;
+private:
+ DeprecatedGenTemplateInstance(Control *control, const Substitution &substitution);
+ FullySpecifiedType gen(Symbol *symbol);
private:
Symbol *_symbol;
- LookupContext _context;
+ Control *_control;
const Substitution _substitution;
};
} // end of namespace CPlusPlus
-#endif // GENTEMPLATEINSTANCE_H
+#endif // CPLUSPLUS_DEPRECATEDGENTEMPLATEINSTANCE_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "DeprecatedLookupContext.h"
+#include "ResolveExpression.h"
+#include "Overview.h"
+#include "CppBindings.h"
+
+#include <CoreTypes.h>
+#include <Symbols.h>
+#include <Literals.h>
+#include <Names.h>
+#include <Scope.h>
+#include <Control.h>
+
+#include <QtDebug>
+
+using namespace CPlusPlus;
+
+/////////////////////////////////////////////////////////////////////
+// LookupContext
+/////////////////////////////////////////////////////////////////////
+DeprecatedLookupContext::DeprecatedLookupContext(Control *control)
+ : _control(control),
+ _symbol(0)
+{ }
+
+DeprecatedLookupContext::DeprecatedLookupContext(Symbol *symbol,
+ Document::Ptr expressionDocument,
+ Document::Ptr thisDocument,
+ const Snapshot &snapshot)
+ : _symbol(symbol),
+ _expressionDocument(expressionDocument),
+ _thisDocument(thisDocument),
+ _snapshot(snapshot)
+{
+ _control = _expressionDocument->control();
+ _visibleScopes = buildVisibleScopes();
+}
+
+bool DeprecatedLookupContext::isValid() const
+{ return _control != 0; }
+
+Control *DeprecatedLookupContext::control() const
+{ return _control; }
+
+Symbol *DeprecatedLookupContext::symbol() const
+{ return _symbol; }
+
+Document::Ptr DeprecatedLookupContext::expressionDocument() const
+{ return _expressionDocument; }
+
+Document::Ptr DeprecatedLookupContext::thisDocument() const
+{ return _thisDocument; }
+
+Document::Ptr DeprecatedLookupContext::document(const QString &fileName) const
+{ return _snapshot.document(fileName); }
+
+Snapshot DeprecatedLookupContext::snapshot() const
+{ return _snapshot; }
+
+bool DeprecatedLookupContext::maybeValidSymbol(Symbol *symbol,
+ ResolveMode mode,
+ const QList<Symbol *> &candidates)
+{
+ if (((mode & ResolveNamespace) && symbol->isNamespace()) ||
+ ((mode & ResolveClass) && symbol->isClass()) ||
+ ((mode & ResolveObjCClass) && symbol->isObjCClass()) ||
+ ((mode & ResolveObjCProtocol) && symbol->isObjCProtocol()) ||
+ (mode & ResolveSymbol)) {
+ return ! candidates.contains(symbol);
+ }
+
+ return false;
+}
+
+QList<Scope *> DeprecatedLookupContext::resolveNestedNameSpecifier(const QualifiedNameId *q,
+ const QList<Scope *> &visibleScopes) const
+{
+ QList<Symbol *> candidates;
+ QList<Scope *> scopes = visibleScopes;
+
+ for (unsigned i = 0; i < q->nameCount() - 1; ++i) {
+ const Name *name = q->nameAt(i);
+
+ candidates = resolveClassOrNamespace(name, scopes);
+
+ if (candidates.isEmpty())
+ break;
+
+ scopes.clear();
+
+ foreach (Symbol *candidate, candidates) {
+ ScopedSymbol *scoped = candidate->asScopedSymbol();
+ Scope *members = scoped->members();
+
+ if (! scopes.contains(members))
+ scopes.append(members);
+ }
+ }
+
+ return scopes;
+}
+
+QList<Symbol *> DeprecatedLookupContext::resolveQualifiedNameId(const QualifiedNameId *q,
+ const QList<Scope *> &visibleScopes,
+ ResolveMode mode) const
+{
+ QList<Symbol *> candidates;
+
+ if (true || mode & ResolveClass) {
+ for (int i = 0; i < visibleScopes.size(); ++i) {
+ Scope *scope = visibleScopes.at(i);
+
+ for (Symbol *symbol = scope->lookat(q); symbol; symbol = symbol->next()) {
+ if (! symbol->name())
+ continue;
+ else if (! symbol->isClass())
+ continue;
+
+ const QualifiedNameId *qq = symbol->name()->asQualifiedNameId();
+
+ if (! qq)
+ continue;
+ else if (! maybeValidSymbol(symbol, mode, candidates))
+ continue;
+
+ if (! q->unqualifiedNameId()->isEqualTo(qq->unqualifiedNameId()))
+ continue;
+
+ else if (qq->nameCount() == q->nameCount()) {
+ unsigned j = 0;
+
+ for (; j < q->nameCount(); ++j) {
+ const Name *classOrNamespaceName1 = q->nameAt(j);
+ const Name *classOrNamespaceName2 = qq->nameAt(j);
+
+ if (! classOrNamespaceName1->isEqualTo(classOrNamespaceName2))
+ break;
+ }
+
+ if (j == q->nameCount())
+ candidates.append(symbol);
+ }
+ }
+ }
+ }
+
+ QList<Scope *> scopes;
+
+ if (q->nameCount() == 1)
+ scopes = visibleScopes; // ### handle global scope lookup
+ else
+ scopes = resolveNestedNameSpecifier(q, visibleScopes);
+
+ QList<Scope *> expanded;
+ foreach (Scope *scope, scopes) {
+ expanded.append(scope);
+
+ for (unsigned i = 0; i < scope->symbolCount(); ++i) {
+ Symbol *member = scope->symbolAt(i);
+
+ if (ScopedSymbol *scopedSymbol = member->asScopedSymbol())
+ expandEnumOrAnonymousSymbol(scopedSymbol, &expanded);
+ }
+ }
+
+ candidates += resolve(q->unqualifiedNameId(), expanded, mode);
+
+ return candidates;
+}
+
+QList<Symbol *> DeprecatedLookupContext::resolveOperatorNameId(const OperatorNameId *opId,
+ const QList<Scope *> &visibleScopes,
+ ResolveMode) const
+{
+ QList<Symbol *> candidates;
+
+ for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) {
+ Scope *scope = visibleScopes.at(scopeIndex);
+
+ for (Symbol *symbol = scope->lookat(opId->kind()); symbol; symbol = symbol->next()) {
+ if (! opId->isEqualTo(symbol->name()))
+ continue;
+
+ if (! candidates.contains(symbol))
+ candidates.append(symbol);
+ }
+ }
+
+ return candidates;
+}
+
+QList<Symbol *> DeprecatedLookupContext::resolve(const Name *name, const QList<Scope *> &visibleScopes,
+ ResolveMode mode) const
+{
+ QList<Symbol *> candidates;
+
+ if (!name)
+ return candidates; // nothing to do, the symbol is anonymous.
+
+ else if (const QualifiedNameId *q = name->asQualifiedNameId())
+ return resolveQualifiedNameId(q, visibleScopes, mode);
+
+ else if (const OperatorNameId *opId = name->asOperatorNameId())
+ return resolveOperatorNameId(opId, visibleScopes, mode);
+
+ else if (const Identifier *id = name->identifier()) {
+ for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) {
+ Scope *scope = visibleScopes.at(scopeIndex);
+
+ for (Symbol *symbol = scope->lookat(id); symbol; symbol = symbol->next()) {
+ if (! symbol->name())
+ continue; // nothing to do, the symbol is anonymous.
+
+ else if (! maybeValidSymbol(symbol, mode, candidates))
+ continue; // skip it, we're not looking for this kind of symbols
+
+ else if (const Identifier *symbolId = symbol->identifier()) {
+ if (! symbolId->isEqualTo(id))
+ continue; // skip it, the symbol's id is not compatible with this lookup.
+ }
+
+ if (const QualifiedNameId *q = symbol->name()->asQualifiedNameId()) {
+
+ if (name->isDestructorNameId() != q->unqualifiedNameId()->isDestructorNameId())
+ continue;
+
+ else if (q->nameCount() > 1) {
+ const Name *classOrNamespaceName = control()->qualifiedNameId(q->names(),
+ q->nameCount() - 1);
+
+ if (const Identifier *classOrNamespaceNameId = identifier(classOrNamespaceName)) {
+ if (classOrNamespaceNameId->isEqualTo(id))
+ continue;
+ }
+
+ const QList<Symbol *> resolvedClassOrNamespace =
+ resolveClassOrNamespace(classOrNamespaceName, visibleScopes);
+
+ bool good = false;
+ foreach (Symbol *classOrNamespace, resolvedClassOrNamespace) {
+ ScopedSymbol *scoped = classOrNamespace->asScopedSymbol();
+ if (visibleScopes.contains(scoped->members())) {
+ good = true;
+ break;
+ }
+ }
+
+ if (! good)
+ continue;
+ }
+ } else if (symbol->name()->isDestructorNameId() != name->isDestructorNameId()) {
+ // ### FIXME: this is wrong!
+ continue;
+ }
+
+ if (! candidates.contains(symbol))
+ candidates.append(symbol);
+ }
+ }
+ }
+
+ return candidates;
+}
+
+const Identifier *DeprecatedLookupContext::identifier(const Name *name) const
+{
+ if (name)
+ return name->identifier();
+
+ return 0;
+}
+
+void DeprecatedLookupContext::buildVisibleScopes_helper(Document::Ptr doc, QList<Scope *> *scopes,
+ QSet<QString> *processed)
+{
+ if (doc && ! processed->contains(doc->fileName())) {
+ processed->insert(doc->fileName());
+
+ if (doc->globalSymbolCount())
+ scopes->append(doc->globalSymbols());
+
+ foreach (const Document::Include &incl, doc->includes()) {
+ buildVisibleScopes_helper(_snapshot.document(incl.fileName()),
+ scopes, processed);
+ }
+ }
+}
+
+QList<Scope *> DeprecatedLookupContext::buildVisibleScopes()
+{
+ QList<Scope *> scopes;
+
+ if (_symbol) {
+ Scope *scope = _symbol->scope();
+
+ if (Function *fun = _symbol->asFunction())
+ scope = fun->members(); // handle ctor initializers.
+
+ for (; scope; scope = scope->enclosingScope()) {
+ if (scope == _thisDocument->globalSymbols())
+ break;
+
+ scopes.append(scope);
+ }
+ }
+
+ QSet<QString> processed;
+ buildVisibleScopes_helper(_thisDocument, &scopes, &processed);
+
+ while (true) {
+ QList<Scope *> expandedScopes;
+ expand(scopes, &expandedScopes);
+
+ if (expandedScopes.size() == scopes.size())
+ return expandedScopes;
+
+ scopes = expandedScopes;
+ }
+
+ return scopes;
+}
+
+QList<Scope *> DeprecatedLookupContext::visibleScopes(const LookupItem &result) const
+{ return visibleScopes(result.declaration()); }
+
+QList<Scope *> DeprecatedLookupContext::visibleScopes(Symbol *symbol) const
+{
+ QList<Scope *> scopes;
+ if (symbol) {
+ for (Scope *scope = symbol->scope(); scope; scope = scope->enclosingScope())
+ scopes.append(scope);
+ }
+ scopes += visibleScopes();
+ scopes = expand(scopes);
+ return scopes;
+}
+
+void DeprecatedLookupContext::expandEnumOrAnonymousSymbol(ScopedSymbol *scopedSymbol,
+ QList<Scope *> *expandedScopes) const
+{
+ if (! scopedSymbol || expandedScopes->contains(scopedSymbol->members()))
+ return;
+
+ Scope *members = scopedSymbol->members();
+
+ if (scopedSymbol->isEnum())
+ expandedScopes->append(members);
+ else if (! scopedSymbol->name() && (scopedSymbol->isClass() || scopedSymbol->isNamespace())) {
+ // anonymous class or namespace
+
+ expandedScopes->append(members);
+
+ for (unsigned i = 0; i < members->symbolCount(); ++i) {
+ Symbol *member = members->symbolAt(i);
+
+ if (ScopedSymbol *nested = member->asScopedSymbol()) {
+ expandEnumOrAnonymousSymbol(nested, expandedScopes);
+ }
+ }
+ }
+}
+
+QList<Scope *> DeprecatedLookupContext::expand(const QList<Scope *> &scopes) const
+{
+ QList<Scope *> expanded;
+ expand(scopes, &expanded);
+ return expanded;
+}
+
+void DeprecatedLookupContext::expand(const QList<Scope *> &scopes, QList<Scope *> *expandedScopes) const
+{
+ for (int i = 0; i < scopes.size(); ++i) {
+ expand(scopes.at(i), scopes, expandedScopes);
+ }
+}
+
+void DeprecatedLookupContext::expandNamespace(Namespace *ns,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const
+{
+ //qDebug() << "*** expand namespace:" << ns->fileName() << ns->line() << ns->column();
+
+ if (Scope *encl = ns->enclosingNamespaceScope())
+ expand(encl, visibleScopes, expandedScopes);
+
+ if (const Name *nsName = ns->name()) {
+ const QList<Symbol *> namespaceList = resolveNamespace(nsName, visibleScopes);
+ foreach (Symbol *otherNs, namespaceList) {
+ if (otherNs == ns)
+ continue;
+ expand(otherNs->asNamespace()->members(), visibleScopes, expandedScopes);
+ }
+ }
+
+ for (unsigned i = 0; i < ns->memberCount(); ++i) { // ### make me fast
+ Symbol *symbol = ns->memberAt(i);
+ if (Namespace *otherNs = symbol->asNamespace()) {
+ if (! otherNs->name()) {
+ expand(otherNs->members(), visibleScopes, expandedScopes);
+ }
+ } else if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) {
+ const QList<Symbol *> candidates = resolveNamespace(u->name(), visibleScopes);
+ for (int j = 0; j < candidates.size(); ++j) {
+ expand(candidates.at(j)->asNamespace()->members(),
+ visibleScopes, expandedScopes);
+ }
+ } else if (Enum *e = symbol->asEnum()) {
+ expand(e->members(), visibleScopes, expandedScopes);
+ }
+ }
+}
+
+void DeprecatedLookupContext::expandClass(Class *klass,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const
+{
+ for (TemplateParameters *params = klass->templateParameters(); params; params = params->previous())
+ expand(params->scope(), visibleScopes, expandedScopes);
+
+ for (unsigned i = 0; i < klass->memberCount(); ++i) {
+ Symbol *symbol = klass->memberAt(i);
+ if (Class *nestedClass = symbol->asClass()) {
+ if (! nestedClass->name()) {
+ expand(nestedClass->members(), visibleScopes, expandedScopes);
+ }
+ } else if (Enum *e = symbol->asEnum()) {
+ expand(e->members(), visibleScopes, expandedScopes);
+ }
+ }
+
+ if (klass->baseClassCount()) {
+ QList<Scope *> classVisibleScopes = visibleScopes;
+ for (Scope *scope = klass->scope(); scope; scope = scope->enclosingScope()) {
+ if (scope->isNamespaceScope()) {
+ Namespace *enclosingNamespace = scope->owner()->asNamespace();
+ if (enclosingNamespace->name()) {
+ const QList<Symbol *> nsList = resolveNamespace(enclosingNamespace->name(),
+ visibleScopes);
+ foreach (Symbol *ns, nsList) {
+ expand(ns->asNamespace()->members(), classVisibleScopes,
+ &classVisibleScopes);
+ }
+ }
+ }
+ }
+
+ for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
+ BaseClass *baseClass = klass->baseClassAt(i);
+ const Name *baseClassName = baseClass->name();
+ const QList<Symbol *> baseClassCandidates = resolveClass(baseClassName,
+ classVisibleScopes);
+
+ for (int j = 0; j < baseClassCandidates.size(); ++j) {
+ if (Class *baseClassSymbol = baseClassCandidates.at(j)->asClass())
+ expand(baseClassSymbol->members(), visibleScopes, expandedScopes);
+ }
+ }
+ }
+}
+
+void DeprecatedLookupContext::expandBlock(Block *blockSymbol,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const
+{
+ for (unsigned i = 0; i < blockSymbol->memberCount(); ++i) {
+ Symbol *symbol = blockSymbol->memberAt(i);
+ if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) {
+ const QList<Symbol *> candidates = resolveNamespace(u->name(),
+ visibleScopes);
+ for (int j = 0; j < candidates.size(); ++j) {
+ expand(candidates.at(j)->asNamespace()->members(),
+ visibleScopes, expandedScopes);
+ }
+ }
+
+ }
+}
+
+void DeprecatedLookupContext::expandFunction(Function *function,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const
+{
+ for (TemplateParameters *params = function->templateParameters(); params; params = params->previous())
+ expand(params->scope(), visibleScopes, expandedScopes);
+
+ if (! expandedScopes->contains(function->arguments()))
+ expandedScopes->append(function->arguments());
+
+ if (const QualifiedNameId *q = function->name()->asQualifiedNameId()) {
+ const Name *nestedNameSpec = 0;
+ if (q->nameCount() == 1)
+ nestedNameSpec = q->nameAt(0);
+ else
+ nestedNameSpec = control()->qualifiedNameId(q->names(), q->nameCount() - 1,
+ q->isGlobal());
+ const QList<Symbol *> candidates = resolveClassOrNamespace(nestedNameSpec, visibleScopes);
+ for (int j = 0; j < candidates.size(); ++j) {
+ if (ScopedSymbol *scopedSymbol = candidates.at(j)->asScopedSymbol())
+ expand(scopedSymbol->members(), visibleScopes, expandedScopes);
+ }
+ }
+}
+
+void DeprecatedLookupContext::expandObjCMethod(ObjCMethod *method,
+ const QList<Scope *> &,
+ QList<Scope *> *expandedScopes) const
+{
+ if (! expandedScopes->contains(method->arguments()))
+ expandedScopes->append(method->arguments());
+}
+
+void DeprecatedLookupContext::expandObjCClass(ObjCClass *klass,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const
+{
+ {// expand other @interfaces, @implementations and categories for this class:
+ const QList<Symbol *> classList = resolveObjCClass(klass->name(), visibleScopes);
+ foreach (Symbol *otherClass, classList) {
+ if (otherClass == klass)
+ continue;
+ expand(otherClass->asObjCClass()->members(), visibleScopes, expandedScopes);
+ }
+ }
+
+ // expand definitions in the currect class:
+ for (unsigned i = 0; i < klass->memberCount(); ++i) {
+ Symbol *symbol = klass->memberAt(i);
+ if (Class *nestedClass = symbol->asClass()) {
+ if (! nestedClass->name()) {
+ expand(nestedClass->members(), visibleScopes, expandedScopes);
+ }
+ } else if (Enum *e = symbol->asEnum()) {
+ expand(e->members(), visibleScopes, expandedScopes);
+ }
+ }
+
+ // expand the base class:
+ if (ObjCBaseClass *baseClass = klass->baseClass()) {
+ const Name *baseClassName = baseClass->name();
+ const QList<Symbol *> baseClassCandidates = resolveObjCClass(baseClassName,
+ visibleScopes);
+
+ for (int j = 0; j < baseClassCandidates.size(); ++j) {
+ if (ObjCClass *baseClassSymbol = baseClassCandidates.at(j)->asObjCClass())
+ expand(baseClassSymbol->members(), visibleScopes, expandedScopes);
+ }
+ }
+
+ // expand the protocols:
+ for (unsigned i = 0; i < klass->protocolCount(); ++i) {
+ const Name *protocolName = klass->protocolAt(i)->name();
+ const QList<Symbol *> protocolCandidates = resolveObjCProtocol(protocolName, visibleScopes);
+ for (int j = 0; j < protocolCandidates.size(); ++j) {
+ if (ObjCProtocol *protocolSymbol = protocolCandidates.at(j)->asObjCProtocol())
+ expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes);
+ }
+ }
+}
+
+void DeprecatedLookupContext::expandObjCProtocol(ObjCProtocol *protocol, const QList<Scope *> &visibleScopes, QList<Scope *> *expandedScopes) const
+{
+ // First expand the protocol itself
+ expand(protocol->members(), visibleScopes, expandedScopes);
+
+ // Then do the same for any incorporated protocol
+ for (unsigned i = 0; i < protocol->protocolCount(); ++i) {
+ ObjCBaseProtocol *baseProtocol = protocol->protocolAt(i);
+ const QList<Symbol *> protocolList = resolveObjCProtocol(baseProtocol->name(), visibleScopes);
+ foreach (Symbol *symbol, protocolList)
+ if (ObjCProtocol *protocolSymbol = symbol->asObjCProtocol())
+ expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes);
+ }
+}
+
+void DeprecatedLookupContext::expand(Scope *scope,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const
+{
+ if (expandedScopes->contains(scope))
+ return;
+
+ expandedScopes->append(scope);
+
+ if (Namespace *ns = scope->owner()->asNamespace()) {
+ expandNamespace(ns, visibleScopes, expandedScopes);
+ } else if (Class *klass = scope->owner()->asClass()) {
+ expandClass(klass, visibleScopes, expandedScopes);
+ } else if (Block *block = scope->owner()->asBlock()) {
+ expandBlock(block, visibleScopes, expandedScopes);
+ } else if (Function *fun = scope->owner()->asFunction()) {
+ expandFunction(fun, visibleScopes, expandedScopes);
+ } else if (ObjCMethod *meth = scope->owner()->asObjCMethod()) {
+ expandObjCMethod(meth, visibleScopes, expandedScopes);
+ } else if (ObjCClass *objcKlass = scope->owner()->asObjCClass()) {
+ expandObjCClass(objcKlass, visibleScopes, expandedScopes);
+ }
+}
+
+static void visibleClassBindings_helper(ClassBinding *classBinding,
+ QList<ClassBinding *> *allClassBindings,
+ QSet<ClassBinding *> *processed)
+{
+ if (! classBinding)
+ return;
+
+ else if (processed->contains(classBinding))
+ return;
+
+ processed->insert(classBinding);
+
+ foreach (ClassBinding *baseClassBinding, classBinding->baseClassBindings)
+ visibleClassBindings_helper(baseClassBinding, allClassBindings, processed);
+
+ allClassBindings->append(classBinding);
+}
+
+static QList<ClassBinding *> visibleClassBindings(Symbol *symbol, NamespaceBinding *globalNamespace)
+{
+ QList<ClassBinding *> classBindings;
+
+ if (! symbol)
+ return classBindings;
+
+ else if (Class *klass = symbol->asClass()) {
+ QSet<ClassBinding *> processed;
+
+ visibleClassBindings_helper(NamespaceBinding::find(klass, globalNamespace),
+ &classBindings, &processed);
+ }
+
+ return classBindings;
+}
+
+Symbol *DeprecatedLookupContext::canonicalSymbol(Symbol *symbol,
+ NamespaceBinding *globalNamespace)
+{
+ Symbol *canonicalSymbol = DeprecatedLookupContext::canonicalSymbol(symbol);
+ if (! canonicalSymbol)
+ return 0;
+
+ if (const Identifier *symbolId = canonicalSymbol->identifier()) {
+ if (symbolId && canonicalSymbol->type()->isFunctionType()) {
+ Class *enclosingClass = canonicalSymbol->scope()->owner()->asClass();
+ const QList<ClassBinding *> classBindings = visibleClassBindings(enclosingClass, globalNamespace);
+
+ foreach (ClassBinding *baseClassBinding, classBindings) {
+ if (! baseClassBinding)
+ continue;
+
+ foreach (Class *baseClass, baseClassBinding->symbols) {
+ if (! baseClass)
+ continue;
+
+ for (Symbol *c = baseClass->members()->lookat(symbolId); c; c = c->next()) {
+ if (! symbolId->isEqualTo(c->identifier()))
+ continue;
+ else if (Function *f = c->type()->asFunctionType()) {
+ if (f->isVirtual())
+ return DeprecatedLookupContext::canonicalSymbol(f);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return canonicalSymbol;
+}
+
+Symbol *DeprecatedLookupContext::canonicalSymbol(const QList<Symbol *> &candidates,
+ NamespaceBinding *globalNamespaceBinding)
+{
+ if (candidates.isEmpty())
+ return 0;
+
+ return canonicalSymbol(candidates.first(), globalNamespaceBinding);
+}
+
+Symbol *DeprecatedLookupContext::canonicalSymbol(const QList<LookupItem> &results,
+ NamespaceBinding *globalNamespaceBinding)
+{
+ QList<Symbol *> candidates;
+
+ foreach (const LookupItem &result, results)
+ candidates.append(result.declaration()); // ### not exactly.
+
+ return canonicalSymbol(candidates, globalNamespaceBinding);
+}
+
+
+Symbol *DeprecatedLookupContext::canonicalSymbol(Symbol *symbol)
+{
+ Symbol *canonical = symbol;
+ Class *canonicalClass = 0;
+ ObjCClass *canonicalObjCClass = 0;
+ ObjCProtocol *canonicalObjCProto = 0;
+
+ for (; symbol; symbol = symbol->next()) {
+ if (symbol->identifier() == canonical->identifier()) {
+ canonical = symbol;
+
+ if (Class *klass = symbol->asClass())
+ canonicalClass = klass;
+ else if (ObjCClass *clazz = symbol->asObjCClass())
+ canonicalObjCClass = clazz;
+ else if (ObjCProtocol *proto = symbol->asObjCProtocol())
+ canonicalObjCProto = proto;
+ }
+ }
+
+ if (canonicalClass) {
+ Q_ASSERT(canonical != 0);
+
+ if (canonical->isForwardClassDeclaration())
+ return canonicalClass; // prefer class declarations when available.
+ } else if (canonicalObjCClass) {
+ Q_ASSERT(canonical != 0);
+
+ if (canonical->isObjCForwardClassDeclaration())
+ return canonicalObjCClass;
+ } else if (canonicalObjCProto) {
+ Q_ASSERT(canonical != 0);
+
+ if (canonical->isObjCForwardProtocolDeclaration())
+ return canonicalObjCProto;
+ }
+
+ if (canonical && canonical->scope()->isClassScope()) {
+ Class *enclosingClass = canonical->scope()->owner()->asClass();
+
+ if (enclosingClass->identifier() == canonical->identifier())
+ return enclosingClass;
+ }
+
+ return canonical;
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 CPLUSPLUS_DEPRECATEDLOOKUPCONTEXT_H
+#define CPLUSPLUS_DEPRECATEDLOOKUPCONTEXT_H
+
+#include "CppDocument.h"
+#include "LookupItem.h"
+#include <FullySpecifiedType.h>
+
+namespace CPlusPlus {
+
+class NamespaceBinding;
+
+class CPLUSPLUS_EXPORT DeprecatedLookupContext
+{
+public:
+ DeprecatedLookupContext(Control *control = 0);
+
+ DeprecatedLookupContext(Symbol *symbol,
+ Document::Ptr expressionDocument,
+ Document::Ptr thisDocument,
+ const Snapshot &snapshot);
+
+ bool isValid() const;
+
+ Control *control() const;
+ Symbol *symbol() const;
+ Document::Ptr expressionDocument() const;
+ Document::Ptr thisDocument() const;
+ Document::Ptr document(const QString &fileName) const;
+ Snapshot snapshot() const;
+
+ static Symbol *canonicalSymbol(const QList<Symbol *> &candidates,
+ NamespaceBinding *globalNamespaceBinding);
+
+ static Symbol *canonicalSymbol(Symbol *symbol,
+ NamespaceBinding *globalNamespaceBinding);
+
+ static Symbol *canonicalSymbol(const QList<LookupItem> &candidates,
+ NamespaceBinding *globalNamespaceBinding);
+
+ QList<Symbol *> resolve(const Name *name) const
+ { return resolve(name, visibleScopes()); }
+
+ QList<Symbol *> resolveNamespace(const Name *name) const
+ { return resolveNamespace(name, visibleScopes()); }
+
+ QList<Symbol *> resolveClass(const Name *name) const
+ { return resolveClass(name, visibleScopes()); }
+
+ QList<Symbol *> resolveClassOrNamespace(const Name *name) const
+ { return resolveClassOrNamespace(name, visibleScopes()); }
+
+ QList<Symbol *> resolveObjCClass(const Name *name) const
+ { return resolveObjCClass(name, visibleScopes()); }
+
+ QList<Symbol *> resolveObjCProtocol(const Name *name) const
+ { return resolveObjCProtocol(name, visibleScopes()); }
+
+ enum ResolveMode {
+ ResolveSymbol = 0x01,
+ ResolveClass = 0x02,
+ ResolveNamespace = 0x04,
+ ResolveClassOrNamespace = ResolveClass | ResolveNamespace,
+ ResolveObjCClass = 0x08,
+ ResolveObjCProtocol = 0x10,
+ ResolveAll = ResolveSymbol | ResolveClassOrNamespace | ResolveObjCClass | ResolveObjCProtocol
+ };
+
+ QList<Symbol *> resolve(const Name *name, const QList<Scope *> &visibleScopes,
+ ResolveMode mode = ResolveAll) const;
+
+ QList<Symbol *> resolveNamespace(const Name *name, const QList<Scope *> &visibleScopes) const
+ { return resolve(name, visibleScopes, ResolveNamespace); }
+
+ QList<Symbol *> resolveClass(const Name *name, const QList<Scope *> &visibleScopes) const
+ { return resolve(name, visibleScopes, ResolveClass); }
+
+ QList<Symbol *> resolveClassOrNamespace(const Name *name, const QList<Scope *> &visibleScopes) const
+ { return resolve(name, visibleScopes, ResolveClassOrNamespace); }
+
+ QList<Symbol *> resolveObjCClass(const Name *name, const QList<Scope *> &visibleScopes) const
+ { return resolve(name, visibleScopes, ResolveObjCClass); }
+
+ QList<Symbol *> resolveObjCProtocol(const Name *name, const QList<Scope *> &visibleScopes) const
+ { return resolve(name, visibleScopes, ResolveObjCProtocol); }
+
+ QList<Scope *> visibleScopes() const
+ { return _visibleScopes; }
+
+ QList<Scope *> visibleScopes(Symbol *symbol) const;
+ QList<Scope *> visibleScopes(const LookupItem &result) const;
+
+ QList<Scope *> expand(const QList<Scope *> &scopes) const;
+
+ void expand(const QList<Scope *> &scopes, QList<Scope *> *expandedScopes) const;
+
+ void expand(Scope *scope, const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandNamespace(Namespace *namespaceSymbol,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandClass(Class *classSymbol,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandBlock(Block *blockSymbol,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandFunction(Function *functionSymbol,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandObjCMethod(ObjCMethod *method,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandObjCClass(ObjCClass *klass,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandObjCProtocol(ObjCProtocol *protocol,
+ const QList<Scope *> &visibleScopes,
+ QList<Scope *> *expandedScopes) const;
+
+ void expandEnumOrAnonymousSymbol(ScopedSymbol *scopedSymbol,
+ QList<Scope *> *expandedScopes) const;
+
+private:
+ static Symbol *canonicalSymbol(Symbol *symbol);
+
+ QList<Symbol *> resolveQualifiedNameId(const QualifiedNameId *q,
+ const QList<Scope *> &visibleScopes,
+ ResolveMode mode) const;
+
+ QList<Symbol *> resolveOperatorNameId(const OperatorNameId *opId,
+ const QList<Scope *> &visibleScopes,
+ ResolveMode mode) const;
+
+ QList<Scope *> resolveNestedNameSpecifier(const QualifiedNameId *q,
+ const QList<Scope *> &visibleScopes) const;
+
+ const Identifier *identifier(const Name *name) const;
+
+ QList<Scope *> buildVisibleScopes();
+
+ void buildVisibleScopes_helper(Document::Ptr doc, QList<Scope *> *scopes,
+ QSet<QString> *processed);
+
+ static bool maybeValidSymbol(Symbol *symbol,
+ ResolveMode mode,
+ const QList<Symbol *> &candidates);
+
+private:
+ Control *_control;
+
+ // The current symbol.
+ Symbol *_symbol;
+
+ // The current expression.
+ Document::Ptr _expressionDocument;
+
+ // The current document.
+ Document::Ptr _thisDocument;
+
+ // All documents.
+ Snapshot _snapshot;
+
+ // Visible scopes.
+ QList<Scope *> _visibleScopes;
+};
+
+} // end of namespace CPlusPlus
+
+#endif // CPLUSPLUS_DEPRECATEDLOOKUPCONTEXT_H
**************************************************************************/
#include "FindUsages.h"
-#include "TypeOfExpression.h"
+#include "DeprecatedLookupContext.h"
#include <Control.h>
#include <Literals.h>
: ASTVisitor(doc->translationUnit()),
_doc(doc),
_snapshot(snapshot),
+ _context(doc, snapshot),
_source(_doc->source()),
_sem(doc->translationUnit()),
_inSimpleDeclaration(0),
_inQProperty(false)
{
_snapshot.insert(_doc);
+ typeofExpression.init(_doc, _snapshot, _context.bindings());
}
void FindUsages::setGlobalNamespaceBinding(NamespaceBindingPtr globalNamespaceBinding)
return matchingLine;
}
+Scope *FindUsages::scopeAt(unsigned tokenIndex) const
+{
+ TranslationUnit *unit = _doc->translationUnit();
+ unsigned line, column;
+ unit->getTokenPosition(tokenIndex, &line, &column);
+ return _doc->scopeAt(line, column);
+}
+
void FindUsages::reportResult(unsigned tokenIndex, const QList<Symbol *> &candidates)
{
if (_processed.contains(tokenIndex))
bool FindUsages::checkCandidates(const QList<Symbol *> &candidates) const
{
- if (Symbol *canonicalSymbol = LookupContext::canonicalSymbol(candidates, _globalNamespaceBinding.data())) {
+ if (Symbol *canonicalSymbol = DeprecatedLookupContext::canonicalSymbol(candidates, _globalNamespaceBinding.data())) {
#if 0
Symbol *c = candidates.first();
return false;
}
-LookupContext FindUsages::currentContext(AST *ast)
-{
- unsigned line, column;
- getTokenStartPosition(ast->firstToken(), &line, &column);
- Symbol *lastVisibleSymbol = _doc->findSymbolAt(line, column);
-
- if (_inQProperty && lastVisibleSymbol->isClass()) {
- Scope *memberScope = lastVisibleSymbol->asClass()->members();
-
- if (unsigned count = memberScope->symbolCount())
- lastVisibleSymbol = memberScope->symbolAt(count - 1);
- }
-
- if (lastVisibleSymbol && lastVisibleSymbol == _previousContext.symbol())
- return _previousContext;
-
- LookupContext ctx(lastVisibleSymbol, _exprDoc, _doc, _snapshot);
- _previousContext = ctx;
- return _previousContext;
-}
-
void FindUsages::ensureNameIsValid(NameAST *ast)
{
if (ast && ! ast->name)
SimpleNameAST *simple = ast->name->asSimpleName();
if (identifier(simple->identifier_token) == _id) {
- LookupContext context = currentContext(ast);
- const QList<Symbol *> candidates = context.resolve(simple->name);
+ const QList<Symbol *> candidates = _context.lookup(simple->name, scopeAt(simple->identifier_token));
reportResult(simple->identifier_token, candidates);
}
}
const QString expression = _source.mid(begin, end - begin);
// qDebug() << "*** check expression:" << expression;
- TypeOfExpression typeofExpression;
- typeofExpression.setSnapshot(_snapshot);
-
unsigned line, column;
getTokenStartPosition(startToken, &line, &column);
- Symbol *lastVisibleSymbol = _doc->findSymbolAt(line, column);
+ Scope *scope = _doc->scopeAt(line, column);
- const QList<LookupItem> results = typeofExpression(expression, _doc, lastVisibleSymbol,
- TypeOfExpression::Preprocess);
+ const QList<LookupItem> results = typeofExpression(expression, scope,
+ TypeOfExpression::Preprocess);
QList<Symbol *> candidates;
foreach (const LookupItem &r, results) {
- Symbol *lastVisibleSymbol = r.lastVisibleSymbol();
+ Symbol *lastVisibleSymbol = r.declaration();
candidates.append(lastVisibleSymbol);
}
{
const Identifier *id = identifier(ast->identifier_token);
if (id == _id) {
- LookupContext context = currentContext(ast);
- const QList<Symbol *> candidates = context.resolve(control()->nameId(id));
+ const QList<Symbol *> candidates = _context.lookup(control()->nameId(id), scopeAt(ast->identifier_token));
reportResult(ast->identifier_token, candidates);
}
{
const Identifier *id = identifier(ast->identifier_token);
if (id == _id) {
- LookupContext context = currentContext(ast);
- const QList<Symbol *> candidates = context.resolve(ast->name);
+ const QList<Symbol *> candidates = _context.lookup(ast->name, scopeAt(ast->identifier_token));
reportResult(ast->identifier_token, candidates);
}
{
const Identifier *id = identifier(ast->identifier_token);
if (id == _id) {
- LookupContext context = currentContext(ast);
- const QList<Symbol *> candidates = context.resolve(ast->name);
+ const QList<Symbol *> candidates = _context.lookup(ast->name, scopeAt(ast->identifier_token));
reportResult(ast->identifier_token, candidates);
}
bool FindUsages::visit(TemplateIdAST *ast)
{
if (_id == identifier(ast->identifier_token)) {
- LookupContext context = currentContext(ast);
- const QList<Symbol *> candidates = context.resolve(ast->name);
+ const QList<Symbol *> candidates = _context.lookup(ast->name, scopeAt(ast->identifier_token));
reportResult(ast->identifier_token, candidates);
}
if (ast->name) {
const Identifier *id = ast->name->identifier();
if (id == _id) {
- LookupContext context = currentContext(ast);
- const QList<Symbol *> candidates = context.resolve(ast->name);
+ const QList<Symbol *> candidates = _context.lookup(ast->name, scopeAt(ast->firstToken()));
reportResult(ast->firstToken(), candidates);
}
}
#include "CppDocument.h"
#include "CppBindings.h"
#include "Semantic.h"
+#include "TypeOfExpression.h"
#include <ASTVisitor.h>
#include <QtCore/QSet>
using ASTVisitor::endVisit;
QString matchingLine(const Token &tk) const;
+ Scope *scopeAt(unsigned tokenIndex) const;
void reportResult(unsigned tokenIndex, const QList<Symbol *> &candidates);
void reportResult(unsigned tokenIndex);
bool checkScope(Symbol *symbol, Symbol *otherSymbol) const;
void checkExpression(unsigned startToken, unsigned endToken);
- LookupContext currentContext(AST *ast);
-
void ensureNameIsValid(NameAST *ast);
virtual bool visit(MemInitializerAST *ast);
Symbol *_declSymbol;
Document::Ptr _doc;
Snapshot _snapshot;
+ LookupContext _context;
QByteArray _source;
Document::Ptr _exprDoc;
Semantic _sem;
QList<QualifiedNameAST *> _qualifiedNameStack;
QList<int> _references;
QList<Usage> _usages;
- LookupContext _previousContext;
int _inSimpleDeclaration;
bool _inQProperty;
QSet<unsigned> _processed;
+ TypeOfExpression typeofExpression;
};
} // end of namespace CPlusPlus
#include "ResolveExpression.h"
#include "Overview.h"
#include "CppBindings.h"
+#include "DeprecatedGenTemplateInstance.h"
#include <CoreTypes.h>
#include <Symbols.h>
#include <QtDebug>
-uint CPlusPlus::qHash(const CPlusPlus::LookupItem &key)
-{
- const uint h1 = QT_PREPEND_NAMESPACE(qHash)(key.type().type());
- const uint h2 = QT_PREPEND_NAMESPACE(qHash)(key.lastVisibleSymbol());
- return ((h1 << 16) | (h1 >> 16)) ^ h2;
+namespace {
+ const bool debug = ! qgetenv("CPLUSPLUS_LOOKUPCONTEXT_DEBUG").isEmpty();
}
using namespace CPlusPlus;
+static void fullyQualifiedName_helper(Symbol *symbol, QList<const Name *> *names)
+{
+ if (! symbol)
+ return;
+
+ fullyQualifiedName_helper(symbol->enclosingSymbol(), names);
+
+ if (symbol->name()) {
+ if (symbol->isClass() || symbol->isNamespace()) {
+ if (const QualifiedNameId *q = symbol->name()->asQualifiedNameId()) {
+ for (unsigned i = 0; i < q->nameCount(); ++i)
+ names->append(q->nameAt(i));
+
+ } else if (symbol->name()->isNameId() || symbol->name()->isTemplateNameId()) {
+ names->append(symbol->name());
+ }
+ } else if (symbol->isObjCClass() || symbol->isObjCBaseClass() || symbol->isObjCProtocol()
+ || symbol->isObjCForwardClassDeclaration() || symbol->isObjCForwardProtocolDeclaration()) {
+ if (symbol->name())
+ names->append(symbol->name());
+ } else if (symbol->isFunction()) {
+ if (const QualifiedNameId *q = symbol->name()->asQualifiedNameId()) {
+ for (unsigned i = 0; i < q->nameCount() - 1; ++i)
+ names->append(q->nameAt(i));
+ }
+ }
+ }
+}
+
+bool ClassOrNamespace::CompareName::operator()(const Name *name, const Name *other) const
+{
+ Q_ASSERT(name != 0);
+ Q_ASSERT(other != 0);
+
+ const Identifier *id = name->identifier();
+ const Identifier *otherId = other->identifier();
+ return strcmp(id->chars(), otherId->chars()) < 0;
+}
+
/////////////////////////////////////////////////////////////////////
// LookupContext
/////////////////////////////////////////////////////////////////////
-LookupContext::LookupContext(Control *control)
- : _control(control),
- _symbol(0)
+LookupContext::LookupContext()
{ }
-LookupContext::LookupContext(Symbol *symbol,
- Document::Ptr expressionDocument,
+LookupContext::LookupContext(Document::Ptr thisDocument,
+ const Snapshot &snapshot)
+ : _expressionDocument(Document::create("<LookupContext>")),
+ _thisDocument(thisDocument),
+ _snapshot(snapshot)
+{
+}
+
+LookupContext::LookupContext(Document::Ptr expressionDocument,
Document::Ptr thisDocument,
const Snapshot &snapshot)
- : _symbol(symbol),
- _expressionDocument(expressionDocument),
+ : _expressionDocument(expressionDocument),
_thisDocument(thisDocument),
_snapshot(snapshot)
{
- _control = _expressionDocument->control();
- _visibleScopes = buildVisibleScopes();
}
-bool LookupContext::isValid() const
-{ return _control != 0; }
+LookupContext::LookupContext(const LookupContext &other)
+ : _expressionDocument(other._expressionDocument),
+ _thisDocument(other._thisDocument),
+ _snapshot(other._snapshot),
+ _bindings(other._bindings)
+{ }
-Control *LookupContext::control() const
-{ return _control; }
+LookupContext &LookupContext::operator = (const LookupContext &other)
+{
+ _expressionDocument = other._expressionDocument;
+ _thisDocument = other._thisDocument;
+ _snapshot = other._snapshot;
+ _bindings = other._bindings;
+ return *this;
+}
+
+QList<const Name *> LookupContext::fullyQualifiedName(Symbol *symbol)
+{
+ QList<const Name *> names;
+ fullyQualifiedName_helper(symbol, &names);
+ return names;
+}
+
+QSharedPointer<CreateBindings> LookupContext::bindings() const
+{
+ if (! _bindings)
+ _bindings = QSharedPointer<CreateBindings>(new CreateBindings(_thisDocument, _snapshot));
+
+ return _bindings;
+}
-Symbol *LookupContext::symbol() const
-{ return _symbol; }
+void LookupContext::setBindings(QSharedPointer<CreateBindings> bindings)
+{
+ _bindings = bindings;
+}
+
+Control *LookupContext::control() const
+{ return bindings()->control(); }
Document::Ptr LookupContext::expressionDocument() const
{ return _expressionDocument; }
Snapshot LookupContext::snapshot() const
{ return _snapshot; }
-bool LookupContext::maybeValidSymbol(Symbol *symbol,
- ResolveMode mode,
- const QList<Symbol *> &candidates)
+ClassOrNamespace *LookupContext::globalNamespace() const
{
- if (((mode & ResolveNamespace) && symbol->isNamespace()) ||
- ((mode & ResolveClass) && symbol->isClass()) ||
- ((mode & ResolveObjCClass) && symbol->isObjCClass()) ||
- ((mode & ResolveObjCProtocol) && symbol->isObjCProtocol()) ||
- (mode & ResolveSymbol)) {
- return ! candidates.contains(symbol);
- }
-
- return false;
+ return bindings()->globalNamespace();
}
-QList<Scope *> LookupContext::resolveNestedNameSpecifier(const QualifiedNameId *q,
- const QList<Scope *> &visibleScopes) const
+ClassOrNamespace *LookupContext::lookupType(const Name *name, Scope *scope) const
{
- QList<Symbol *> candidates;
- QList<Scope *> scopes = visibleScopes;
-
- for (unsigned i = 0; i < q->nameCount() - 1; ++i) {
- const Name *name = q->nameAt(i);
+ if (ClassOrNamespace *b = bindings()->lookupType(scope->owner()))
+ return b->lookupType(name);
- candidates = resolveClassOrNamespace(name, scopes);
-
- if (candidates.isEmpty())
- break;
-
- scopes.clear();
-
- foreach (Symbol *candidate, candidates) {
- ScopedSymbol *scoped = candidate->asScopedSymbol();
- Scope *members = scoped->members();
-
- if (! scopes.contains(members))
- scopes.append(members);
- }
- }
+ return 0;
+}
- return scopes;
+ClassOrNamespace *LookupContext::lookupType(Symbol *symbol) const
+{
+ return bindings()->lookupType(symbol);
}
-QList<Symbol *> LookupContext::resolveQualifiedNameId(const QualifiedNameId *q,
- const QList<Scope *> &visibleScopes,
- ResolveMode mode) const
+QList<Symbol *> LookupContext::lookup(const Name *name, Scope *scope) const
{
QList<Symbol *> candidates;
- if (true || mode & ResolveClass) {
- for (int i = 0; i < visibleScopes.size(); ++i) {
- Scope *scope = visibleScopes.at(i);
+ if (! name)
+ return candidates;
- for (Symbol *symbol = scope->lookat(q); symbol; symbol = symbol->next()) {
- if (! symbol->name())
- continue;
- else if (! symbol->isClass())
- continue;
+ const Identifier *id = name->identifier();
- const QualifiedNameId *qq = symbol->name()->asQualifiedNameId();
+ for (; scope; scope = scope->enclosingScope()) {
+ if (id && scope->isBlockScope()) {
+ bindings()->lookupInScope(name, scope, &candidates, /*templateId = */ 0);
- if (! qq)
- continue;
- else if (! maybeValidSymbol(symbol, mode, candidates))
- continue;
-
- if (! q->unqualifiedNameId()->isEqualTo(qq->unqualifiedNameId()))
- continue;
+ if (! candidates.isEmpty())
+ break; // it's a local.
- else if (qq->nameCount() == q->nameCount()) {
- unsigned j = 0;
+ for (unsigned index = 0; index < scope->symbolCount(); ++index) {
+ Symbol *member = scope->symbolAt(index);
- for (; j < q->nameCount(); ++j) {
- const Name *classOrNamespaceName1 = q->nameAt(j);
- const Name *classOrNamespaceName2 = qq->nameAt(j);
+ if (UsingNamespaceDirective *u = member->asUsingNamespaceDirective()) {
+ if (Namespace *enclosingNamespace = u->enclosingNamespaceScope()->owner()->asNamespace()) {
+ if (ClassOrNamespace *b = bindings()->lookupType(enclosingNamespace)) {
+ if (ClassOrNamespace *uu = b->lookupType(u->name())) {
+ candidates = uu->lookup(name);
- if (! classOrNamespaceName1->isEqualTo(classOrNamespaceName2))
- break;
+ if (! candidates.isEmpty())
+ return candidates;
+ }
+ }
}
-
- if (j == q->nameCount())
- candidates.append(symbol);
}
}
- }
- }
- QList<Scope *> scopes;
+ } else if (scope->isFunctionScope()) {
+ Function *fun = scope->owner()->asFunction();
+ bindings()->lookupInScope(name, fun->arguments(), &candidates, /*templateId = */ 0);
+ if (! candidates.isEmpty())
+ break; // it's a formal argument.
- if (q->nameCount() == 1)
- scopes = visibleScopes; // ### handle global scope lookup
- else
- scopes = resolveNestedNameSpecifier(q, visibleScopes);
+ if (fun->name() && fun->name()->isQualifiedNameId()) {
+ if (ClassOrNamespace *binding = bindings()->lookupType(fun))
+ return binding->lookup(name);
+ }
- QList<Scope *> expanded;
- foreach (Scope *scope, scopes) {
- expanded.append(scope);
+ } else if (scope->isObjCMethodScope()) {
+ ObjCMethod *method = scope->owner()->asObjCMethod();
+ bindings()->lookupInScope(name, method->arguments(), &candidates, /*templateId = */ 0);
+ if (! candidates.isEmpty())
+ break; // it's a formal argument.
- for (unsigned i = 0; i < scope->symbolCount(); ++i) {
- Symbol *member = scope->symbolAt(i);
+ } else if (scope->isClassScope() || scope->isNamespaceScope()
+ || scope->isObjCClassScope() || scope->isObjCProtocolScope()) {
+ if (ClassOrNamespace *binding = bindings()->lookupType(scope->owner()))
+ return binding->lookup(name);
- if (ScopedSymbol *scopedSymbol = member->asScopedSymbol())
- expandEnumOrAnonymousSymbol(scopedSymbol, &expanded);
+ break;
}
}
- candidates += resolve(q->unqualifiedNameId(), expanded, mode);
-
return candidates;
}
-QList<Symbol *> LookupContext::resolveOperatorNameId(const OperatorNameId *opId,
- const QList<Scope *> &visibleScopes,
- ResolveMode) const
+ClassOrNamespace::ClassOrNamespace(CreateBindings *factory, ClassOrNamespace *parent)
+ : _factory(factory), _parent(parent), _templateId(0)
{
- QList<Symbol *> candidates;
+}
- for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) {
- Scope *scope = visibleScopes.at(scopeIndex);
+const TemplateNameId *ClassOrNamespace::templateId() const
+{
+ return _templateId;
+}
- for (Symbol *symbol = scope->lookat(opId->kind()); symbol; symbol = symbol->next()) {
- if (! opId->isEqualTo(symbol->name()))
- continue;
+ClassOrNamespace *ClassOrNamespace::parent() const
+{
+ return _parent;
+}
- if (! candidates.contains(symbol))
- candidates.append(symbol);
- }
- }
+QList<ClassOrNamespace *> ClassOrNamespace::usings() const
+{
+ const_cast<ClassOrNamespace *>(this)->flush();
+ return _usings;
+}
- return candidates;
+QList<Enum *> ClassOrNamespace::enums() const
+{
+ const_cast<ClassOrNamespace *>(this)->flush();
+ return _enums;
}
-QList<Symbol *> LookupContext::resolve(const Name *name, const QList<Scope *> &visibleScopes,
- ResolveMode mode) const
+QList<Symbol *> ClassOrNamespace::symbols() const
{
- QList<Symbol *> candidates;
+ if (_templateId && ! _usings.isEmpty())
+ return _usings.first()->symbols(); // ask to the base implementation
- if (!name)
- return candidates; // nothing to do, the symbol is anonymous.
+ const_cast<ClassOrNamespace *>(this)->flush();
+ return _symbols;
+}
- else if (const QualifiedNameId *q = name->asQualifiedNameId())
- return resolveQualifiedNameId(q, visibleScopes, mode);
+ClassOrNamespace *ClassOrNamespace::globalNamespace() const
+{
+ ClassOrNamespace *e = const_cast<ClassOrNamespace *>(this);
- else if (const OperatorNameId *opId = name->asOperatorNameId())
- return resolveOperatorNameId(opId, visibleScopes, mode);
+ do {
+ if (! e->_parent)
+ break;
- else if (const Identifier *id = name->identifier()) {
- for (int scopeIndex = 0; scopeIndex < visibleScopes.size(); ++scopeIndex) {
- Scope *scope = visibleScopes.at(scopeIndex);
+ e = e->_parent;
+ } while (e);
- for (Symbol *symbol = scope->lookat(id); symbol; symbol = symbol->next()) {
- if (! symbol->name())
- continue; // nothing to do, the symbol is anonymous.
+ return e;
+}
- else if (! maybeValidSymbol(symbol, mode, candidates))
- continue; // skip it, we're not looking for this kind of symbols
+QList<Symbol *> ClassOrNamespace::find(const Name *name)
+{
+ return lookup_helper(name, false);
+}
- else if (const Identifier *symbolId = symbol->identifier()) {
- if (! symbolId->isEqualTo(id))
- continue; // skip it, the symbol's id is not compatible with this lookup.
- }
+QList<Symbol *> ClassOrNamespace::lookup(const Name *name)
+{
+ return lookup_helper(name, true);
+}
- if (const QualifiedNameId *q = symbol->name()->asQualifiedNameId()) {
+QList<Symbol *> ClassOrNamespace::lookup_helper(const Name *name, bool searchInEnclosingScope)
+{
+ QList<Symbol *> result;
- if (name->isDestructorNameId() != q->unqualifiedNameId()->isDestructorNameId())
- continue;
+ if (name) {
+ if (const QualifiedNameId *q = name->asQualifiedNameId()) {
+ ClassOrNamespace *binding = this;
- else if (q->nameCount() > 1) {
- const Name *classOrNamespaceName = control()->qualifiedNameId(q->names(),
- q->nameCount() - 1);
+ if (q->isGlobal())
+ binding = globalNamespace();
- if (const Identifier *classOrNamespaceNameId = identifier(classOrNamespaceName)) {
- if (classOrNamespaceNameId->isEqualTo(id))
- continue;
- }
+ if (q->nameCount() == 1)
+ return binding->find(q->unqualifiedNameId());
- const QList<Symbol *> resolvedClassOrNamespace =
- resolveClassOrNamespace(classOrNamespaceName, visibleScopes);
+ binding = binding->lookupType(q->nameAt(0));
- bool good = false;
- foreach (Symbol *classOrNamespace, resolvedClassOrNamespace) {
- ScopedSymbol *scoped = classOrNamespace->asScopedSymbol();
- if (visibleScopes.contains(scoped->members())) {
- good = true;
- break;
- }
- }
+ for (unsigned index = 1; binding && index < q->nameCount() - 1; ++index)
+ binding = binding->findType(q->nameAt(index));
- if (! good)
- continue;
- }
- } else if (symbol->name()->isDestructorNameId() != name->isDestructorNameId()) {
- // ### FIXME: this is wrong!
- continue;
- }
+ if (binding)
+ result = binding->find(q->unqualifiedNameId());
- if (! candidates.contains(symbol))
- candidates.append(symbol);
- }
+ return result;
}
+
+ QSet<ClassOrNamespace *> processed;
+ ClassOrNamespace *binding = this;
+ do {
+ lookup_helper(name, binding, &result, &processed, /*templateId = */ 0);
+ binding = binding->_parent;
+ } while (searchInEnclosingScope && binding);
}
- return candidates;
+ return result;
}
-const Identifier *LookupContext::identifier(const Name *name) const
+void ClassOrNamespace::lookup_helper(const Name *name, ClassOrNamespace *binding,
+ QList<Symbol *> *result,
+ QSet<ClassOrNamespace *> *processed,
+ const TemplateNameId *templateId)
{
- if (name)
- return name->identifier();
+ if (binding && ! processed->contains(binding)) {
+ processed->insert(binding);
- return 0;
+ foreach (Symbol *s, binding->symbols()) {
+ if (ScopedSymbol *scoped = s->asScopedSymbol())
+ _factory->lookupInScope(name, scoped->members(), result, templateId);
+ }
+
+ foreach (Enum *e, binding->enums())
+ _factory->lookupInScope(name, e->members(), result, templateId);
+
+ foreach (ClassOrNamespace *u, binding->usings())
+ lookup_helper(name, u, result, processed, binding->_templateId);
+ }
}
-void LookupContext::buildVisibleScopes_helper(Document::Ptr doc, QList<Scope *> *scopes,
- QSet<QString> *processed)
+void CreateBindings::lookupInScope(const Name *name, Scope *scope,
+ QList<Symbol *> *result,
+ const TemplateNameId *templateId)
{
- if (doc && ! processed->contains(doc->fileName())) {
- processed->insert(doc->fileName());
+ Q_UNUSED(templateId);
- if (doc->globalSymbolCount())
- scopes->append(doc->globalSymbols());
+ if (! name) {
+ return;
- foreach (const Document::Include &incl, doc->includes()) {
- buildVisibleScopes_helper(_snapshot.document(incl.fileName()),
- scopes, processed);
+ } else if (const OperatorNameId *op = name->asOperatorNameId()) {
+ for (Symbol *s = scope->lookat(op->kind()); s; s = s->next()) {
+ if (! s->name())
+ continue;
+ else if (! s->name()->isEqualTo(op))
+ continue;
+
+ result->append(s);
}
+
+ } else if (const Identifier *id = name->identifier()) {
+ for (Symbol *s = scope->lookat(id); s; s = s->next()) {
+ if (! id->isEqualTo(s->identifier()))
+ continue;
+ else if (s->name()->isQualifiedNameId())
+ continue; // skip qualified ids.
+
+#if 0
+ if (templateId && (s->isDeclaration() || s->isFunction())) {
+
+ FullySpecifiedType ty = GenTemplateInstance::instantiate(templateId, s, _control);
+
+ if (debug) {
+ Overview oo;
+ oo.setShowFunctionSignatures(true);
+ oo.setShowReturnTypes(true);
+ qDebug() << "instantiate:" << oo(s->type(), s->name()) << "using:" << oo(templateId) << oo(ty);
+ }
+
+ if (Declaration *decl = s->asDeclaration()) {
+ Declaration *d = _control->newDeclaration(0, 0);
+ d->copy(decl);
+ d->setType(ty);
+ result->append(d);
+ continue;
+ } else if (Function *fun = s->asFunction()) {
+ Function *d = ty->asFunctionType();
+ d->copy(fun);
+ result->append(d);
+ continue;
+ }
+ }
+#endif
+
+ result->append(s);
+ }
+
}
}
-QList<Scope *> LookupContext::buildVisibleScopes()
+ClassOrNamespace *ClassOrNamespace::lookupType(const Name *name)
{
- QList<Scope *> scopes;
+ if (! name)
+ return 0;
- if (_symbol) {
- Scope *scope = _symbol->scope();
+ QSet<ClassOrNamespace *> processed;
+ return lookupType_helper(name, &processed, /*searchInEnclosingScope =*/ true);
+}
- if (Function *fun = _symbol->asFunction())
- scope = fun->members(); // handle ctor initializers.
+ClassOrNamespace *ClassOrNamespace::findType(const Name *name)
+{
+ QSet<ClassOrNamespace *> processed;
+ return lookupType_helper(name, &processed, /*searchInEnclosingScope =*/ false);
+}
- for (; scope; scope = scope->enclosingScope()) {
- if (scope == _thisDocument->globalSymbols())
- break;
+ClassOrNamespace *ClassOrNamespace::lookupType_helper(const Name *name,
+ QSet<ClassOrNamespace *> *processed,
+ bool searchInEnclosingScope)
+{
+ if (const QualifiedNameId *q = name->asQualifiedNameId()) {
+ ClassOrNamespace *e = this;
- scopes.append(scope);
- }
- }
+ if (q->isGlobal())
+ e = globalNamespace();
- QSet<QString> processed;
- buildVisibleScopes_helper(_thisDocument, &scopes, &processed);
+ e = e->lookupType(q->nameAt(0));
- while (true) {
- QList<Scope *> expandedScopes;
- expand(scopes, &expandedScopes);
+ for (unsigned index = 1; e && index < q->nameCount(); ++index)
+ e = e->findType(q->nameAt(index));
- if (expandedScopes.size() == scopes.size())
- return expandedScopes;
+ return e;
- scopes = expandedScopes;
- }
+ } else if (! processed->contains(this)) {
+ processed->insert(this);
- return scopes;
-}
+ if (name->isNameId() || name->isTemplateNameId()) {
+ flush();
-QList<Scope *> LookupContext::visibleScopes(const LookupItem &result) const
-{ return visibleScopes(result.lastVisibleSymbol()); }
+ if (ClassOrNamespace *e = nestedType(name))
+ return e;
-QList<Scope *> LookupContext::visibleScopes(Symbol *symbol) const
-{
- QList<Scope *> scopes;
- if (symbol) {
- for (Scope *scope = symbol->scope(); scope; scope = scope->enclosingScope())
- scopes.append(scope);
+ foreach (ClassOrNamespace *u, usings()) {
+ if (ClassOrNamespace *r = u->lookupType_helper(name, processed, /*searchInEnclosingScope =*/ false))
+ return r;
+ }
+ }
+
+ if (_parent && searchInEnclosingScope)
+ return _parent->lookupType_helper(name, processed, searchInEnclosingScope);
}
- scopes += visibleScopes();
- scopes = expand(scopes);
- return scopes;
+
+ return 0;
}
-void LookupContext::expandEnumOrAnonymousSymbol(ScopedSymbol *scopedSymbol,
- QList<Scope *> *expandedScopes) const
+ClassOrNamespace *ClassOrNamespace::nestedType(const Name *name) const
{
- if (! scopedSymbol || expandedScopes->contains(scopedSymbol->members()))
- return;
+ Q_ASSERT(name != 0);
+ Q_ASSERT(name->isNameId() || name->isTemplateNameId());
- Scope *members = scopedSymbol->members();
+ const_cast<ClassOrNamespace *>(this)->flush();
- if (scopedSymbol->isEnum())
- expandedScopes->append(members);
- else if (! scopedSymbol->name() && (scopedSymbol->isClass() || scopedSymbol->isNamespace())) {
- // anonymous class or namespace
+ Table::const_iterator it = _classOrNamespaces.find(name);
- expandedScopes->append(members);
+ if (it == _classOrNamespaces.end())
+ return 0;
- for (unsigned i = 0; i < members->symbolCount(); ++i) {
- Symbol *member = members->symbolAt(i);
+ ClassOrNamespace *c = it->second;
- if (ScopedSymbol *nested = member->asScopedSymbol()) {
- expandEnumOrAnonymousSymbol(nested, expandedScopes);
- }
+ if (const TemplateNameId *templId = name->asTemplateNameId()) {
+ foreach (ClassOrNamespace *i, c->_instantiations) {
+ if (templId->isEqualTo(i->_templateId))
+ return i;
}
+
+ ClassOrNamespace *i = _factory->allocClassOrNamespace(c);
+ i->_templateId = templId;
+ i->_usings.append(c);
+ c->_instantiations.append(i);
+ return i;
}
+
+ return c;
}
-QList<Scope *> LookupContext::expand(const QList<Scope *> &scopes) const
+void ClassOrNamespace::flush()
{
- QList<Scope *> expanded;
- expand(scopes, &expanded);
- return expanded;
+ if (! _todo.isEmpty()) {
+ const QList<Symbol *> todo = _todo;
+ _todo.clear();
+
+ foreach (Symbol *member, todo)
+ _factory->process(member, this);
+ }
}
-void LookupContext::expand(const QList<Scope *> &scopes, QList<Scope *> *expandedScopes) const
+void ClassOrNamespace::addSymbol(Symbol *symbol)
{
- for (int i = 0; i < scopes.size(); ++i) {
- expand(scopes.at(i), scopes, expandedScopes);
- }
+ _symbols.append(symbol);
}
-void LookupContext::expandNamespace(Namespace *ns,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const
+void ClassOrNamespace::addTodo(Symbol *symbol)
{
- //qDebug() << "*** expand namespace:" << ns->fileName() << ns->line() << ns->column();
+ _todo.append(symbol);
+}
- if (Scope *encl = ns->enclosingNamespaceScope())
- expand(encl, visibleScopes, expandedScopes);
+void ClassOrNamespace::addEnum(Enum *e)
+{
+ _enums.append(e);
+}
- if (const Name *nsName = ns->name()) {
- const QList<Symbol *> namespaceList = resolveNamespace(nsName, visibleScopes);
- foreach (Symbol *otherNs, namespaceList) {
- if (otherNs == ns)
- continue;
- expand(otherNs->asNamespace()->members(), visibleScopes, expandedScopes);
- }
- }
+void ClassOrNamespace::addUsing(ClassOrNamespace *u)
+{
+ _usings.append(u);
+}
- for (unsigned i = 0; i < ns->memberCount(); ++i) { // ### make me fast
- Symbol *symbol = ns->memberAt(i);
- if (Namespace *otherNs = symbol->asNamespace()) {
- if (! otherNs->name()) {
- expand(otherNs->members(), visibleScopes, expandedScopes);
- }
- } else if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) {
- const QList<Symbol *> candidates = resolveNamespace(u->name(), visibleScopes);
- for (int j = 0; j < candidates.size(); ++j) {
- expand(candidates.at(j)->asNamespace()->members(),
- visibleScopes, expandedScopes);
- }
- } else if (Enum *e = symbol->asEnum()) {
- expand(e->members(), visibleScopes, expandedScopes);
- }
- }
+void ClassOrNamespace::addNestedType(const Name *alias, ClassOrNamespace *e)
+{
+ _classOrNamespaces[alias] = e;
}
-void LookupContext::expandClass(Class *klass,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const
+ClassOrNamespace *ClassOrNamespace::findOrCreateType(const Name *name)
{
- for (TemplateParameters *params = klass->templateParameters(); params; params = params->previous())
- expand(params->scope(), visibleScopes, expandedScopes);
+ if (! name)
+ return this;
- for (unsigned i = 0; i < klass->memberCount(); ++i) {
- Symbol *symbol = klass->memberAt(i);
- if (Class *nestedClass = symbol->asClass()) {
- if (! nestedClass->name()) {
- expand(nestedClass->members(), visibleScopes, expandedScopes);
- }
- } else if (Enum *e = symbol->asEnum()) {
- expand(e->members(), visibleScopes, expandedScopes);
- }
- }
+ if (const QualifiedNameId *q = name->asQualifiedNameId()) {
+ ClassOrNamespace *e = this;
- if (klass->baseClassCount()) {
- QList<Scope *> classVisibleScopes = visibleScopes;
- for (Scope *scope = klass->scope(); scope; scope = scope->enclosingScope()) {
- if (scope->isNamespaceScope()) {
- Namespace *enclosingNamespace = scope->owner()->asNamespace();
- if (enclosingNamespace->name()) {
- const QList<Symbol *> nsList = resolveNamespace(enclosingNamespace->name(),
- visibleScopes);
- foreach (Symbol *ns, nsList) {
- expand(ns->asNamespace()->members(), classVisibleScopes,
- &classVisibleScopes);
- }
- }
- }
- }
+ for (unsigned i = 0; e && i < q->nameCount(); ++i)
+ e = e->findOrCreateType(q->nameAt(i));
- for (unsigned i = 0; i < klass->baseClassCount(); ++i) {
- BaseClass *baseClass = klass->baseClassAt(i);
- const Name *baseClassName = baseClass->name();
- const QList<Symbol *> baseClassCandidates = resolveClass(baseClassName,
- classVisibleScopes);
+ return e;
- for (int j = 0; j < baseClassCandidates.size(); ++j) {
- if (Class *baseClassSymbol = baseClassCandidates.at(j)->asClass())
- expand(baseClassSymbol->members(), visibleScopes, expandedScopes);
- }
+ } else if (name->isNameId() || name->isTemplateNameId()) {
+ ClassOrNamespace *e = nestedType(name);
+
+ if (! e) {
+ e = _factory->allocClassOrNamespace(this);
+ _classOrNamespaces[name] = e;
}
+
+ return e;
}
+
+ return 0;
}
-void LookupContext::expandBlock(Block *blockSymbol,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const
+CreateBindings::CreateBindings(Document::Ptr thisDocument, const Snapshot &snapshot)
+ : _snapshot(snapshot)
{
- for (unsigned i = 0; i < blockSymbol->memberCount(); ++i) {
- Symbol *symbol = blockSymbol->memberAt(i);
- if (UsingNamespaceDirective *u = symbol->asUsingNamespaceDirective()) {
- const QList<Symbol *> candidates = resolveNamespace(u->name(),
- visibleScopes);
- for (int j = 0; j < candidates.size(); ++j) {
- expand(candidates.at(j)->asNamespace()->members(),
- visibleScopes, expandedScopes);
- }
- }
+ _control = new Control();
+ _globalNamespace = allocClassOrNamespace(/*parent = */ 0);
+ _currentClassOrNamespace = _globalNamespace;
- }
+ process(thisDocument);
}
-void LookupContext::expandFunction(Function *function,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const
+CreateBindings::~CreateBindings()
{
- for (TemplateParameters *params = function->templateParameters(); params; params = params->previous())
- expand(params->scope(), visibleScopes, expandedScopes);
-
- if (! expandedScopes->contains(function->arguments()))
- expandedScopes->append(function->arguments());
+ qDeleteAll(_entities);
+ delete _control;
+}
- if (const QualifiedNameId *q = function->name()->asQualifiedNameId()) {
- const Name *nestedNameSpec = 0;
- if (q->nameCount() == 1)
- nestedNameSpec = q->nameAt(0);
- else
- nestedNameSpec = control()->qualifiedNameId(q->names(), q->nameCount() - 1,
- q->isGlobal());
- const QList<Symbol *> candidates = resolveClassOrNamespace(nestedNameSpec, visibleScopes);
- for (int j = 0; j < candidates.size(); ++j) {
- if (ScopedSymbol *scopedSymbol = candidates.at(j)->asScopedSymbol())
- expand(scopedSymbol->members(), visibleScopes, expandedScopes);
- }
- }
+ClassOrNamespace *CreateBindings::switchCurrentClassOrNamespace(ClassOrNamespace *classOrNamespace)
+{
+ ClassOrNamespace *previous = _currentClassOrNamespace;
+ _currentClassOrNamespace = classOrNamespace;
+ return previous;
}
-void LookupContext::expandObjCMethod(ObjCMethod *method,
- const QList<Scope *> &,
- QList<Scope *> *expandedScopes) const
+ClassOrNamespace *CreateBindings::globalNamespace() const
{
- if (! expandedScopes->contains(method->arguments()))
- expandedScopes->append(method->arguments());
+ return _globalNamespace;
}
-void LookupContext::expandObjCClass(ObjCClass *klass,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const
+ClassOrNamespace *CreateBindings::lookupType(Symbol *symbol)
{
- {// expand other @interfaces, @implementations and categories for this class:
- const QList<Symbol *> classList = resolveObjCClass(klass->name(), visibleScopes);
- foreach (Symbol *otherClass, classList) {
- if (otherClass == klass)
- continue;
- expand(otherClass->asObjCClass()->members(), visibleScopes, expandedScopes);
- }
- }
+ const QList<const Name *> names = LookupContext::fullyQualifiedName(symbol);
- // expand definitions in the currect class:
- for (unsigned i = 0; i < klass->memberCount(); ++i) {
- Symbol *symbol = klass->memberAt(i);
- if (Class *nestedClass = symbol->asClass()) {
- if (! nestedClass->name()) {
- expand(nestedClass->members(), visibleScopes, expandedScopes);
- }
- } else if (Enum *e = symbol->asEnum()) {
- expand(e->members(), visibleScopes, expandedScopes);
- }
- }
+ if (names.isEmpty())
+ return _globalNamespace;
- // expand the base class:
- if (ObjCBaseClass *baseClass = klass->baseClass()) {
- const Name *baseClassName = baseClass->name();
- const QList<Symbol *> baseClassCandidates = resolveObjCClass(baseClassName,
- visibleScopes);
+ ClassOrNamespace *b = _globalNamespace->lookupType(names.at(0));
- for (int j = 0; j < baseClassCandidates.size(); ++j) {
- if (ObjCClass *baseClassSymbol = baseClassCandidates.at(j)->asObjCClass())
- expand(baseClassSymbol->members(), visibleScopes, expandedScopes);
- }
- }
+ for (int i = 1; b && i < names.size(); ++i)
+ b = b->findType(names.at(i));
- // expand the protocols:
- for (unsigned i = 0; i < klass->protocolCount(); ++i) {
- const Name *protocolName = klass->protocolAt(i)->name();
- const QList<Symbol *> protocolCandidates = resolveObjCProtocol(protocolName, visibleScopes);
- for (int j = 0; j < protocolCandidates.size(); ++j) {
- if (ObjCProtocol *protocolSymbol = protocolCandidates.at(j)->asObjCProtocol())
- expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes);
- }
- }
+ return b;
}
-void LookupContext::expandObjCProtocol(ObjCProtocol *protocol, const QList<Scope *> &visibleScopes, QList<Scope *> *expandedScopes) const
+void CreateBindings::process(Symbol *s, ClassOrNamespace *classOrNamespace)
{
- // First expand the protocol itself
- expand(protocol->members(), visibleScopes, expandedScopes);
+ ClassOrNamespace *previous = switchCurrentClassOrNamespace(classOrNamespace);
+ accept(s);
+ (void) switchCurrentClassOrNamespace(previous);
+}
- // Then do the same for any incorporated protocol
- for (unsigned i = 0; i < protocol->protocolCount(); ++i) {
- ObjCBaseProtocol *baseProtocol = protocol->protocolAt(i);
- const QList<Symbol *> protocolList = resolveObjCProtocol(baseProtocol->name(), visibleScopes);
- foreach (Symbol *symbol, protocolList)
- if (ObjCProtocol *protocolSymbol = symbol->asObjCProtocol())
- expandObjCProtocol(protocolSymbol, visibleScopes, expandedScopes);
- }
+void CreateBindings::process(Symbol *symbol)
+{
+ _currentClassOrNamespace->addTodo(symbol);
}
-void LookupContext::expand(Scope *scope,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const
+Control *CreateBindings::control() const
{
- if (expandedScopes->contains(scope))
- return;
+ return _control;
+}
- expandedScopes->append(scope);
-
- if (Namespace *ns = scope->owner()->asNamespace()) {
- expandNamespace(ns, visibleScopes, expandedScopes);
- } else if (Class *klass = scope->owner()->asClass()) {
- expandClass(klass, visibleScopes, expandedScopes);
- } else if (Block *block = scope->owner()->asBlock()) {
- expandBlock(block, visibleScopes, expandedScopes);
- } else if (Function *fun = scope->owner()->asFunction()) {
- expandFunction(fun, visibleScopes, expandedScopes);
- } else if (ObjCMethod *meth = scope->owner()->asObjCMethod()) {
- expandObjCMethod(meth, visibleScopes, expandedScopes);
- } else if (ObjCClass *objcKlass = scope->owner()->asObjCClass()) {
- expandObjCClass(objcKlass, visibleScopes, expandedScopes);
- }
+ClassOrNamespace *CreateBindings::allocClassOrNamespace(ClassOrNamespace *parent)
+{
+ ClassOrNamespace *e = new ClassOrNamespace(this, parent);
+ _entities.append(e);
+ return e;
}
-static void visibleClassBindings_helper(ClassBinding *classBinding,
- QList<ClassBinding *> *allClassBindings,
- QSet<ClassBinding *> *processed)
+void CreateBindings::process(Document::Ptr doc)
{
- if (! classBinding)
+ if (! doc)
return;
- else if (processed->contains(classBinding))
- return;
+ else if (Namespace *globalNamespace = doc->globalNamespace()) {
+ if (! _processed.contains(globalNamespace)) {
+ _processed.insert(globalNamespace);
+
+ foreach (const Document::Include &i, doc->includes()) {
+ if (Document::Ptr incl = _snapshot.document(i.fileName()))
+ process(incl);
+ }
- processed->insert(classBinding);
+ accept(globalNamespace);
+ }
+ }
+}
- foreach (ClassBinding *baseClassBinding, classBinding->baseClassBindings)
- visibleClassBindings_helper(baseClassBinding, allClassBindings, processed);
+ClassOrNamespace *CreateBindings::enterClassOrNamespaceBinding(Symbol *symbol)
+{
+ ClassOrNamespace *entity = _currentClassOrNamespace->findOrCreateType(symbol->name());
+ entity->addSymbol(symbol);
- allClassBindings->append(classBinding);
+ return switchCurrentClassOrNamespace(entity);
}
-static QList<ClassBinding *> visibleClassBindings(Symbol *symbol, NamespaceBinding *globalNamespace)
+ClassOrNamespace *CreateBindings::enterGlobalClassOrNamespace(Symbol *symbol)
{
- QList<ClassBinding *> classBindings;
+ ClassOrNamespace *entity = _globalNamespace->findOrCreateType(symbol->name());
+ entity->addSymbol(symbol);
- if (! symbol)
- return classBindings;
+ return switchCurrentClassOrNamespace(entity);
+}
- else if (Class *klass = symbol->asClass()) {
- QSet<ClassBinding *> processed;
+bool CreateBindings::visit(Namespace *ns)
+{
+ ClassOrNamespace *previous = enterClassOrNamespaceBinding(ns);
- visibleClassBindings_helper(NamespaceBinding::find(klass, globalNamespace),
- &classBindings, &processed);
- }
+ for (unsigned i = 0; i < ns->memberCount(); ++i)
+ process(ns->memberAt(i));
- return classBindings;
+ _currentClassOrNamespace = previous;
+ return false;
}
-Symbol *LookupContext::canonicalSymbol(Symbol *symbol,
- NamespaceBinding *globalNamespace)
+bool CreateBindings::visit(Class *klass)
{
- Symbol *canonicalSymbol = LookupContext::canonicalSymbol(symbol);
- if (! canonicalSymbol)
- return 0;
+ ClassOrNamespace *previous = _currentClassOrNamespace;
+ ClassOrNamespace *binding = 0;
- if (const Identifier *symbolId = canonicalSymbol->identifier()) {
- if (symbolId && canonicalSymbol->type()->isFunctionType()) {
- Class *enclosingClass = canonicalSymbol->scope()->owner()->asClass();
- const QList<ClassBinding *> classBindings = visibleClassBindings(enclosingClass, globalNamespace);
+ if (klass->name() && klass->name()->isQualifiedNameId())
+ binding = _currentClassOrNamespace->lookupType(klass->name());
- foreach (ClassBinding *baseClassBinding, classBindings) {
- if (! baseClassBinding)
- continue;
+ if (! binding)
+ binding = _currentClassOrNamespace->findOrCreateType(klass->name());
- foreach (Class *baseClass, baseClassBinding->symbols) {
- if (! baseClass)
- continue;
+ _currentClassOrNamespace = binding;
+ _currentClassOrNamespace->addSymbol(klass);
- for (Symbol *c = baseClass->members()->lookat(symbolId); c; c = c->next()) {
- if (! symbolId->isEqualTo(c->identifier()))
- continue;
- else if (Function *f = c->type()->asFunctionType()) {
- if (f->isVirtual())
- return LookupContext::canonicalSymbol(f);
- }
- }
+ for (unsigned i = 0; i < klass->baseClassCount(); ++i)
+ process(klass->baseClassAt(i));
+
+ for (unsigned i = 0; i < klass->memberCount(); ++i)
+ process(klass->memberAt(i));
+
+ _currentClassOrNamespace = previous;
+ return false;
+}
+
+bool CreateBindings::visit(ForwardClassDeclaration *klass)
+{
+ if (! klass->isFriend()) {
+ ClassOrNamespace *previous = enterClassOrNamespaceBinding(klass);
+ _currentClassOrNamespace = previous;
+ }
+
+ return false;
+}
+
+bool CreateBindings::visit(Enum *e)
+{
+ _currentClassOrNamespace->addEnum(e);
+ return false;
+}
+
+bool CreateBindings::visit(Declaration *decl)
+{
+ if (decl->isTypedef()) {
+ const FullySpecifiedType ty = decl->type();
+ const Identifier *typedefId = decl->identifier();
+
+ if (typedefId && ! (ty.isConst() || ty.isVolatile())) {
+ if (const NamedType *namedTy = ty->asNamedType()) {
+ if (ClassOrNamespace *e = _currentClassOrNamespace->lookupType(namedTy->name())) {
+ _currentClassOrNamespace->addNestedType(decl->name(), e);
+ } else if (false) {
+ Overview oo;
+ qDebug() << "found entity not found for" << oo(namedTy->name());
}
}
}
}
- return canonicalSymbol;
+ return false;
}
-Symbol *LookupContext::canonicalSymbol(const QList<Symbol *> &candidates,
- NamespaceBinding *globalNamespaceBinding)
+bool CreateBindings::visit(Function *)
{
- if (candidates.isEmpty())
- return 0;
+ return false;
+}
- return canonicalSymbol(candidates.first(), globalNamespaceBinding);
+bool CreateBindings::visit(BaseClass *b)
+{
+ if (ClassOrNamespace *base = _currentClassOrNamespace->lookupType(b->name())) {
+ _currentClassOrNamespace->addUsing(base);
+ } else if (false) {
+ Overview oo;
+ qDebug() << "no entity for:" << oo(b->name());
+ }
+ return false;
}
-Symbol *LookupContext::canonicalSymbol(const QList<LookupItem> &results,
- NamespaceBinding *globalNamespaceBinding)
+bool CreateBindings::visit(UsingNamespaceDirective *u)
{
- QList<Symbol *> candidates;
+ if (ClassOrNamespace *e = _currentClassOrNamespace->lookupType(u->name())) {
+ _currentClassOrNamespace->addUsing(e);
+ } else if (false) {
+ Overview oo;
+ qDebug() << "no entity for namespace:" << oo(u->name());
+ }
+ return false;
+}
- foreach (const LookupItem &result, results)
- candidates.append(result.lastVisibleSymbol()); // ### not exactly.
+bool CreateBindings::visit(NamespaceAlias *a)
+{
+ if (! a->identifier()) {
+ return false;
- return canonicalSymbol(candidates, globalNamespaceBinding);
-}
+ } else if (ClassOrNamespace *e = _currentClassOrNamespace->lookupType(a->namespaceName())) {
+ if (a->name()->isNameId() || a->name()->isTemplateNameId())
+ _currentClassOrNamespace->addNestedType(a->name(), e);
+ } else if (false) {
+ Overview oo;
+ qDebug() << "no entity for namespace:" << oo(a->namespaceName());
+ }
-Symbol *LookupContext::canonicalSymbol(Symbol *symbol)
+ return false;
+}
+
+bool CreateBindings::visit(ObjCClass *klass)
{
- Symbol *canonical = symbol;
- Class *canonicalClass = 0;
- ObjCClass *canonicalObjCClass = 0;
- ObjCProtocol *canonicalObjCProto = 0;
+ ClassOrNamespace *previous = enterGlobalClassOrNamespace(klass);
- for (; symbol; symbol = symbol->next()) {
- if (symbol->identifier() == canonical->identifier()) {
- canonical = symbol;
+ process(klass->baseClass());
- if (Class *klass = symbol->asClass())
- canonicalClass = klass;
- else if (ObjCClass *clazz = symbol->asObjCClass())
- canonicalObjCClass = clazz;
- else if (ObjCProtocol *proto = symbol->asObjCProtocol())
- canonicalObjCProto = proto;
- }
+ for (unsigned i = 0; i < klass->protocolCount(); ++i)
+ process(klass->protocolAt(i));
+
+ for (unsigned i = 0; i < klass->memberCount(); ++i)
+ process(klass->memberAt(i));
+
+ _currentClassOrNamespace = previous;
+ return false;
+}
+
+bool CreateBindings::visit(ObjCBaseClass *b)
+{
+ if (ClassOrNamespace *base = _globalNamespace->lookupType(b->name())) {
+ _currentClassOrNamespace->addUsing(base);
+ } else if (false) {
+ Overview oo;
+ qDebug() << "no entity for:" << oo(b->name());
}
+ return false;
+}
- if (canonicalClass) {
- Q_ASSERT(canonical != 0);
+bool CreateBindings::visit(ObjCForwardClassDeclaration *klass)
+{
+ ClassOrNamespace *previous = enterGlobalClassOrNamespace(klass);
+ _currentClassOrNamespace = previous;
+ return false;
+}
- if (canonical->isForwardClassDeclaration())
- return canonicalClass; // prefer class declarations when available.
- } else if (canonicalObjCClass) {
- Q_ASSERT(canonical != 0);
+bool CreateBindings::visit(ObjCProtocol *proto)
+{
+ ClassOrNamespace *previous = enterGlobalClassOrNamespace(proto);
- if (canonical->isObjCForwardClassDeclaration())
- return canonicalObjCClass;
- } else if (canonicalObjCProto) {
- Q_ASSERT(canonical != 0);
+ for (unsigned i = 0; i < proto->protocolCount(); ++i)
+ process(proto->protocolAt(i));
- if (canonical->isObjCForwardProtocolDeclaration())
- return canonicalObjCProto;
- }
+ for (unsigned i = 0; i < proto->memberCount(); ++i)
+ process(proto->memberAt(i));
- if (canonical && canonical->scope()->isClassScope()) {
- Class *enclosingClass = canonical->scope()->owner()->asClass();
+ _currentClassOrNamespace = previous;
+ return false;
+}
- if (enclosingClass->identifier() == canonical->identifier())
- return enclosingClass;
+bool CreateBindings::visit(ObjCBaseProtocol *b)
+{
+ if (ClassOrNamespace *base = _globalNamespace->lookupType(b->name())) {
+ _currentClassOrNamespace->addUsing(base);
+ } else if (false) {
+ Overview oo;
+ qDebug() << "no entity for:" << oo(b->name());
}
+ return false;
+}
+
+bool CreateBindings::visit(ObjCForwardProtocolDeclaration *proto)
+{
+ ClassOrNamespace *previous = enterGlobalClassOrNamespace(proto);
+ _currentClassOrNamespace = previous;
+ return false;
+}
- return canonical;
+bool CreateBindings::visit(ObjCMethod *)
+{
+ return false;
}
#define CPLUSPLUS_LOOKUPCONTEXT_H
#include "CppDocument.h"
+#include "LookupItem.h"
#include <FullySpecifiedType.h>
+#include <Type.h>
+#include <SymbolVisitor.h>
+#include <QtCore/QSet>
+#include <map>
+#include <functional>
namespace CPlusPlus {
-class CPLUSPLUS_EXPORT LookupItem
-{
-public:
- LookupItem()
- : _lastVisibleSymbol(0) {}
-
- LookupItem(const FullySpecifiedType &type, Symbol *lastVisibleSymbol)
- : _type(type), _lastVisibleSymbol(lastVisibleSymbol) {}
-
- FullySpecifiedType type() const { return _type; }
- void setType(const FullySpecifiedType &type) { _type = type; }
-
- Symbol *lastVisibleSymbol() const { return _lastVisibleSymbol; }
- void setLastVisibleSymbol(Symbol *symbol) { _lastVisibleSymbol = symbol; }
-
- bool operator == (const LookupItem &other) const
- {
- if (_type == other._type)
- return _lastVisibleSymbol == other._lastVisibleSymbol;
-
- return false;
- }
+class CreateBindings;
- bool operator != (const LookupItem &result) const
- { return ! operator == (result); }
-
-private:
- FullySpecifiedType _type;
- Symbol *_lastVisibleSymbol;
-};
-
-class CPLUSPLUS_EXPORT LookupContext
+class CPLUSPLUS_EXPORT ClassOrNamespace
{
public:
- LookupContext(Control *control = 0);
-
- LookupContext(Symbol *symbol,
- Document::Ptr expressionDocument,
- Document::Ptr thisDocument,
- const Snapshot &snapshot);
+ ClassOrNamespace(CreateBindings *factory, ClassOrNamespace *parent);
- bool isValid() const;
+ const TemplateNameId *templateId() const;
+ ClassOrNamespace *parent() const;
+ QList<ClassOrNamespace *> usings() const;
+ QList<Enum *> enums() const;
+ QList<Symbol *> symbols() const;
- Control *control() const;
- Symbol *symbol() const;
- Document::Ptr expressionDocument() const;
- Document::Ptr thisDocument() const;
- Document::Ptr document(const QString &fileName) const;
- Snapshot snapshot() const;
+ ClassOrNamespace *globalNamespace() const;
- static Symbol *canonicalSymbol(const QList<Symbol *> &candidates,
- NamespaceBinding *globalNamespaceBinding);
+ QList<Symbol *> lookup(const Name *name);
+ QList<Symbol *> find(const Name *name);
- static Symbol *canonicalSymbol(Symbol *symbol,
- NamespaceBinding *globalNamespaceBinding);
+ ClassOrNamespace *lookupType(const Name *name);
+ ClassOrNamespace *findType(const Name *name);
- static Symbol *canonicalSymbol(const QList<LookupItem> &candidates,
- NamespaceBinding *globalNamespaceBinding);
+private:
+ /// \internal
+ void flush();
- QList<Symbol *> resolve(const Name *name) const
- { return resolve(name, visibleScopes()); }
+ /// \internal
+ ClassOrNamespace *findOrCreateType(const Name *name);
- QList<Symbol *> resolveNamespace(const Name *name) const
- { return resolveNamespace(name, visibleScopes()); }
+ void addTodo(Symbol *symbol);
+ void addSymbol(Symbol *symbol);
+ void addEnum(Enum *e);
+ void addUsing(ClassOrNamespace *u);
+ void addNestedType(const Name *alias, ClassOrNamespace *e);
- QList<Symbol *> resolveClass(const Name *name) const
- { return resolveClass(name, visibleScopes()); }
+ QList<Symbol *> lookup_helper(const Name *name, bool searchInEnclosingScope);
- QList<Symbol *> resolveClassOrNamespace(const Name *name) const
- { return resolveClassOrNamespace(name, visibleScopes()); }
+ void lookup_helper(const Name *name, ClassOrNamespace *binding,
+ QList<Symbol *> *result,
+ QSet<ClassOrNamespace *> *processed,
+ const TemplateNameId *templateId);
- QList<Symbol *> resolveObjCClass(const Name *name) const
- { return resolveObjCClass(name, visibleScopes()); }
+ ClassOrNamespace *lookupType_helper(const Name *name, QSet<ClassOrNamespace *> *processed,
+ bool searchInEnclosingScope);
- QList<Symbol *> resolveObjCProtocol(const Name *name) const
- { return resolveObjCProtocol(name, visibleScopes()); }
+ ClassOrNamespace *nestedType(const Name *name) const;
- enum ResolveMode {
- ResolveSymbol = 0x01,
- ResolveClass = 0x02,
- ResolveNamespace = 0x04,
- ResolveClassOrNamespace = ResolveClass | ResolveNamespace,
- ResolveObjCClass = 0x08,
- ResolveObjCProtocol = 0x10,
- ResolveAll = ResolveSymbol | ResolveClassOrNamespace | ResolveObjCClass | ResolveObjCProtocol
+private:
+ struct CompareName: std::binary_function<const Name *, const Name *, bool> {
+ bool operator()(const Name *name, const Name *other) const;
};
- QList<Symbol *> resolve(const Name *name, const QList<Scope *> &visibleScopes,
- ResolveMode mode = ResolveAll) const;
-
- QList<Symbol *> resolveNamespace(const Name *name, const QList<Scope *> &visibleScopes) const
- { return resolve(name, visibleScopes, ResolveNamespace); }
-
- QList<Symbol *> resolveClass(const Name *name, const QList<Scope *> &visibleScopes) const
- { return resolve(name, visibleScopes, ResolveClass); }
-
- QList<Symbol *> resolveClassOrNamespace(const Name *name, const QList<Scope *> &visibleScopes) const
- { return resolve(name, visibleScopes, ResolveClassOrNamespace); }
-
- QList<Symbol *> resolveObjCClass(const Name *name, const QList<Scope *> &visibleScopes) const
- { return resolve(name, visibleScopes, ResolveObjCClass); }
-
- QList<Symbol *> resolveObjCProtocol(const Name *name, const QList<Scope *> &visibleScopes) const
- { return resolve(name, visibleScopes, ResolveObjCProtocol); }
-
- QList<Scope *> visibleScopes() const
- { return _visibleScopes; }
-
- QList<Scope *> visibleScopes(Symbol *symbol) const;
- QList<Scope *> visibleScopes(const LookupItem &result) const;
-
- QList<Scope *> expand(const QList<Scope *> &scopes) const;
+private:
+ typedef std::map<const Name *, ClassOrNamespace *, CompareName> Table;
+ CreateBindings *_factory;
+ ClassOrNamespace *_parent;
+ QList<Symbol *> _symbols;
+ QList<ClassOrNamespace *> _usings;
+ Table _classOrNamespaces;
+ QList<Enum *> _enums;
+ QList<Symbol *> _todo;
+
+ // it's an instantiation.
+ const TemplateNameId *_templateId;
+
+ // templates
+ QList<ClassOrNamespace *> _instantiations;
+
+ friend class CreateBindings;
+};
- void expand(const QList<Scope *> &scopes, QList<Scope *> *expandedScopes) const;
+class CPLUSPLUS_EXPORT CreateBindings: protected SymbolVisitor
+{
+ Q_DISABLE_COPY(CreateBindings)
- void expand(Scope *scope, const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+public:
+ CreateBindings(Document::Ptr thisDocument, const Snapshot &snapshot);
+ virtual ~CreateBindings();
- void expandNamespace(Namespace *namespaceSymbol,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+ /// Returns the binding for the global namespace.
+ ClassOrNamespace *globalNamespace() const;
- void expandClass(Class *classSymbol,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+ /// Finds the binding associated to the given symbol.
+ ClassOrNamespace *lookupType(Symbol *symbol);
- void expandBlock(Block *blockSymbol,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+ /// Returns the Control that must be used to create temporary symbols.
+ /// \internal
+ Control *control() const;
- void expandFunction(Function *functionSymbol,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+ /// Searches in \a scope for symbols with the given \a name.
+ /// Store the result in \a results.
+ /// \internal
+ void lookupInScope(const Name *name, Scope *scope, QList<Symbol *> *result,
+ const TemplateNameId *templateId);
+
+ /// Create bindings for the symbols reachable from \a rootSymbol.
+ /// \internal
+ void process(Symbol *rootSymbol, ClassOrNamespace *classOrNamespace);
+
+ /// Create an empty ClassOrNamespace binding with the given \a parent.
+ /// \internal
+ ClassOrNamespace *allocClassOrNamespace(ClassOrNamespace *parent);
+
+protected:
+ using SymbolVisitor::visit;
+
+ /// Change the current ClassOrNamespace binding.
+ ClassOrNamespace *switchCurrentClassOrNamespace(ClassOrNamespace *classOrNamespace);
+
+ /// Enters the ClassOrNamespace binding associated with the given \a symbol.
+ ClassOrNamespace *enterClassOrNamespaceBinding(Symbol *symbol);
+
+ /// Enters a ClassOrNamespace binding for the given \a symbol in the global
+ /// namespace binding.
+ ClassOrNamespace *enterGlobalClassOrNamespace(Symbol *symbol);
+
+ /// Creates bindings for the given \a document.
+ void process(Document::Ptr document);
+
+ /// Creates bindings for the symbols reachable from the \a root symbol.
+ void process(Symbol *root);
+
+ virtual bool visit(Namespace *ns);
+ virtual bool visit(Class *klass);
+ virtual bool visit(ForwardClassDeclaration *klass);
+ virtual bool visit(Enum *e);
+ virtual bool visit(Declaration *decl);
+ virtual bool visit(Function *);
+ virtual bool visit(BaseClass *b);
+ virtual bool visit(UsingNamespaceDirective *u);
+ virtual bool visit(NamespaceAlias *a);
+
+ virtual bool visit(ObjCClass *klass);
+ virtual bool visit(ObjCBaseClass *b);
+ virtual bool visit(ObjCForwardClassDeclaration *klass);
+ virtual bool visit(ObjCProtocol *proto);
+ virtual bool visit(ObjCBaseProtocol *b);
+ virtual bool visit(ObjCForwardProtocolDeclaration *proto);
+ virtual bool visit(ObjCMethod *);
- void expandObjCMethod(ObjCMethod *method,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+private:
+ Control *_control;
+ Snapshot _snapshot;
+ QSet<Namespace *> _processed;
+ QList<ClassOrNamespace *> _entities;
+ ClassOrNamespace *_globalNamespace;
+ ClassOrNamespace *_currentClassOrNamespace;
+};
- void expandObjCClass(ObjCClass *klass,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+class CPLUSPLUS_EXPORT LookupContext
+{
+public:
+ LookupContext();
- void expandObjCProtocol(ObjCProtocol *protocol,
- const QList<Scope *> &visibleScopes,
- QList<Scope *> *expandedScopes) const;
+ LookupContext(Document::Ptr thisDocument,
+ const Snapshot &snapshot);
- void expandEnumOrAnonymousSymbol(ScopedSymbol *scopedSymbol,
- QList<Scope *> *expandedScopes) const;
+ LookupContext(Document::Ptr expressionDocument,
+ Document::Ptr thisDocument,
+ const Snapshot &snapshot);
-private:
- static Symbol *canonicalSymbol(Symbol *symbol);
+ LookupContext(const LookupContext &other);
+ LookupContext &operator = (const LookupContext &other);
- QList<Symbol *> resolveQualifiedNameId(const QualifiedNameId *q,
- const QList<Scope *> &visibleScopes,
- ResolveMode mode) const;
+ Document::Ptr expressionDocument() const;
+ Document::Ptr thisDocument() const;
+ Document::Ptr document(const QString &fileName) const;
+ Snapshot snapshot() const;
- QList<Symbol *> resolveOperatorNameId(const OperatorNameId *opId,
- const QList<Scope *> &visibleScopes,
- ResolveMode mode) const;
+ ClassOrNamespace *globalNamespace() const;
- QList<Scope *> resolveNestedNameSpecifier(const QualifiedNameId *q,
- const QList<Scope *> &visibleScopes) const;
+ QList<Symbol *> lookup(const Name *name, Scope *scope) const;
+ ClassOrNamespace *lookupType(const Name *name, Scope *scope) const;
+ ClassOrNamespace *lookupType(Symbol *symbol) const;
- const Identifier *identifier(const Name *name) const;
+ /// \internal
+ QSharedPointer<CreateBindings> bindings() const;
- QList<Scope *> buildVisibleScopes();
+ /// \internal
+ void setBindings(QSharedPointer<CreateBindings> bindings);
- void buildVisibleScopes_helper(Document::Ptr doc, QList<Scope *> *scopes,
- QSet<QString> *processed);
+ Control *control() const; // ### deprecate
- static bool maybeValidSymbol(Symbol *symbol,
- ResolveMode mode,
- const QList<Symbol *> &candidates);
+ static QList<const Name *> fullyQualifiedName(Symbol *symbol);
private:
- Control *_control;
-
- // The current symbol.
- Symbol *_symbol;
-
// The current expression.
Document::Ptr _expressionDocument;
// All documents.
Snapshot _snapshot;
- // Visible scopes.
- QList<Scope *> _visibleScopes;
+ // Bindings
+ mutable QSharedPointer<CreateBindings> _bindings;
};
-uint qHash(const CPlusPlus::LookupItem &result);
-
} // end of namespace CPlusPlus
-#if defined(Q_CC_MSVC) && _MSC_VER <= 1300
-//this ensures that code outside QmlJS can use the hash function
-//it also a workaround for some compilers
-inline uint qHash(const CPlusPlus::LookupItem &item) { return CPlusPlus::qHash(item); }
-#endif
-
#endif // CPLUSPLUS_LOOKUPCONTEXT_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "LookupItem.h"
+#include <FullySpecifiedType.h>
+#include <Symbol.h>
+#include <Control.h>
+
+#include <QtDebug>
+
+using namespace CPlusPlus;
+
+uint CPlusPlus::qHash(const CPlusPlus::LookupItem &key)
+{
+ const uint h1 = QT_PREPEND_NAMESPACE(qHash)(key.type().type());
+ const uint h2 = QT_PREPEND_NAMESPACE(qHash)(key.scope());
+ return ((h1 << 16) | (h1 >> 16)) ^ h2;
+}
+
+LookupItem::LookupItem()
+ : _scope(0), _declaration(0)
+{ }
+
+FullySpecifiedType LookupItem::type() const
+{ return _type; }
+
+void LookupItem::setType(const FullySpecifiedType &type)
+{ _type = type; }
+
+Symbol *LookupItem::declaration() const
+{ return _declaration; }
+
+void LookupItem::setDeclaration(Symbol *declaration)
+{ _declaration = declaration; }
+
+Scope *LookupItem::scope() const
+{
+ if (! _scope && _declaration)
+ return _declaration->scope();
+
+ return _scope;
+}
+
+void LookupItem::setScope(Scope *scope)
+{ _scope = scope; }
+
+bool LookupItem::operator == (const LookupItem &other) const
+{
+ if (_type == other._type && _declaration == other._declaration && _scope == other._scope)
+ return true;
+
+ return false;
+}
+
+bool LookupItem::operator != (const LookupItem &result) const
+{ return ! operator == (result); }
--- /dev/null
+/**************************************************************************
+**
+** 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 CPLUSPLUS_LOOKUPITEM_H
+#define CPLUSPLUS_LOOKUPITEM_H
+
+#include <FullySpecifiedType.h>
+#include <QtCore/QHash>
+
+namespace CPlusPlus {
+
+class CPLUSPLUS_EXPORT LookupItem
+{
+public:
+ /// Constructs an null LookupItem.
+ LookupItem();
+
+ /// Returns this item's type.
+ FullySpecifiedType type() const;
+
+ /// Sets this item's type.
+ void setType(const FullySpecifiedType &type);
+
+ /// Returns the last visible symbol.
+ Symbol *declaration() const;
+
+ /// Sets the last visible symbol.
+ void setDeclaration(Symbol *declaration);
+
+ /// Returns this item's scope.
+ Scope *scope() const;
+
+ /// Sets this item's scope.
+ void setScope(Scope *scope);
+
+ bool operator == (const LookupItem &other) const;
+ bool operator != (const LookupItem &other) const;
+
+private:
+ FullySpecifiedType _type;
+ Scope *_scope;
+ Symbol *_declaration;
+};
+
+uint qHash(const CPlusPlus::LookupItem &result);
+
+} // end of namespace CPlusPlus
+
+#if defined(Q_CC_MSVC) && _MSC_VER <= 1300
+//this ensures that code outside QmlJS can use the hash function
+//it also a workaround for some compilers
+inline uint qHash(const CPlusPlus::LookupItem &item) { return CPlusPlus::qHash(item); }
+#endif
+
+#endif // CPLUSPLUS_LOOKUPITEM_H
#include "ResolveExpression.h"
#include "LookupContext.h"
#include "Overview.h"
-#include "GenTemplateInstance.h"
+#include "DeprecatedGenTemplateInstance.h"
#include <Control.h>
#include <AST.h>
#include <NameVisitor.h>
#include <QtCore/QList>
-#include <QtCore/QVarLengthArray>
#include <QtCore/QtDebug>
using namespace CPlusPlus;
namespace {
+const bool debug = ! qgetenv("CPLUSPLUS_DEBUG").isEmpty();
+
template <typename _Tp>
static QList<_Tp> removeDuplicates(const QList<_Tp> &results)
{
/////////////////////////////////////////////////////////////////////
ResolveExpression::ResolveExpression(const LookupContext &context)
: ASTVisitor(context.expressionDocument()->translationUnit()),
+ _scope(0),
_context(context),
sem(context.expressionDocument()->translationUnit())
{ }
ResolveExpression::~ResolveExpression()
{ }
-QList<LookupItem> ResolveExpression::operator()(ExpressionAST *ast)
+QList<LookupItem> ResolveExpression::operator()(ExpressionAST *ast, Scope *scope)
+{ return resolve(ast, scope); }
+
+QList<LookupItem> ResolveExpression::resolve(ExpressionAST *ast, Scope *scope)
+{
+ Q_ASSERT(scope != 0);
+
+ Scope *previousVisibleSymbol = _scope;
+ _scope = scope;
+ const QList<LookupItem> result = resolve(ast);
+ _scope = previousVisibleSymbol;
+ return result;
+}
+
+QList<LookupItem> ResolveExpression::resolve(ExpressionAST *ast)
{
const QList<LookupItem> previousResults = switchResults(QList<LookupItem>());
accept(ast);
return removeDuplicates(switchResults(previousResults));
}
-QList<LookupItem>
-ResolveExpression::switchResults(const QList<LookupItem> &results)
+QList<LookupItem> ResolveExpression::switchResults(const QList<LookupItem> &results)
{
const QList<LookupItem> previousResults = _results;
_results = results;
return previousResults;
}
-void ResolveExpression::addResults(const QList<LookupItem> &results)
+void ResolveExpression::addResults(const QList<Symbol *> &symbols)
{
- foreach (const LookupItem r, results)
- addResult(r);
+ foreach (Symbol *symbol, symbols) {
+ LookupItem item;
+ item.setType(symbol->type());
+ item.setScope(symbol->scope());
+ item.setDeclaration(symbol);
+ _results.append(item);
+ }
}
-void ResolveExpression::addResult(const FullySpecifiedType &ty, Symbol *symbol)
-{ return addResult(LookupItem(ty, symbol)); }
-
-void ResolveExpression::addResult(const LookupItem &r)
+void ResolveExpression::addResult(const FullySpecifiedType &ty, Scope *scope)
{
- LookupItem p = r;
+ LookupItem item;
+ item.setType(ty);
+ item.setScope(scope);
- if (! p.lastVisibleSymbol())
- p.setLastVisibleSymbol(_context.symbol());
-
- if (! _results.contains(p))
- _results.append(p);
+ _results.append(item);
}
-QList<Scope *> ResolveExpression::visibleScopes(const LookupItem &result) const
-{ return _context.visibleScopes(result); }
-
bool ResolveExpression::visit(BinaryExpressionAST *ast)
{
if (tokenKind(ast->binary_op_token) == T_COMMA && ast->right_expression && ast->right_expression->asQtMethod() != 0) {
QtMethodAST *qtMethod = ast->right_expression->asQtMethod();
if (DeclaratorAST *d = qtMethod->declarator) {
if (d->core_declarator) {
- if (DeclaratorIdAST *declaratorId = d->core_declarator->asDeclaratorId())
- if (NameAST *nameAST = declaratorId->name)
- _results = resolveMemberExpression(_results, T_ARROW, nameAST->name);
+ if (DeclaratorIdAST *declaratorId = d->core_declarator->asDeclaratorId()) {
+ if (NameAST *nameAST = declaratorId->name) {
+ if (ClassOrNamespace *binding = baseExpression(_results, T_ARROW)) {
+ _results.clear();
+ addResults(binding->lookup(nameAST->name));
+ }
+ }
+ }
}
}
bool ResolveExpression::visit(CastExpressionAST *ast)
{
- addResult(sem.check(ast->type_id, _context.expressionDocument()->globalSymbols()));
+ Scope *dummyScope = _context.expressionDocument()->globalSymbols();
+ FullySpecifiedType ty = sem.check(ast->type_id, dummyScope);
+ addResult(ty, _scope);
return false;
}
bool ResolveExpression::visit(CppCastExpressionAST *ast)
{
- addResult(sem.check(ast->type_id, _context.expressionDocument()->globalSymbols()));
+ Scope *dummyScope = _context.expressionDocument()->globalSymbols();
+ FullySpecifiedType ty = sem.check(ast->type_id, dummyScope);
+ addResult(ty, _scope);
return false;
}
bool ResolveExpression::visit(DeleteExpressionAST *)
{
FullySpecifiedType ty(control()->voidType());
- addResult(ty);
+ addResult(ty, _scope);
return false;
}
bool ResolveExpression::visit(NewExpressionAST *ast)
{
if (ast->new_type_id) {
- Scope *scope = _context.expressionDocument()->globalSymbols();
- FullySpecifiedType ty = sem.check(ast->new_type_id->type_specifier_list, scope);
- ty = sem.check(ast->new_type_id->ptr_operator_list, ty, scope);
+ Scope *dummyScope = _context.expressionDocument()->globalSymbols();
+ FullySpecifiedType ty = sem.check(ast->new_type_id->type_specifier_list, dummyScope);
+ ty = sem.check(ast->new_type_id->ptr_operator_list, ty, dummyScope);
FullySpecifiedType ptrTy(control()->pointerType(ty));
- addResult(ptrTy);
+ addResult(ptrTy, _scope);
}
// nothing to do.
return false;
const Name *q = control()->qualifiedNameId(std_type_info, 2, /*global=*/ true);
FullySpecifiedType ty(control()->namedType(q));
- addResult(ty);
+ addResult(ty, _scope);
return false;
}
{
accept(ast->base_expression);
- for (PostfixListAST *it = ast->postfix_expression_list; it; it = it->next) {
+ for (PostfixListAST *it = ast->postfix_expression_list; it; it = it->next)
accept(it->value);
- }
return false;
}
{
FullySpecifiedType ty(control()->integerType(IntegerType::Int));
ty.setUnsigned(true);
- addResult(ty);
+ addResult(ty, _scope);
return false;
}
if (literal->isUnsigned())
ty.setUnsigned(true);
- addResult(ty);
+ addResult(ty, _scope);
return false;
}
bool ResolveExpression::visit(BoolLiteralAST *)
{
FullySpecifiedType ty(control()->integerType(IntegerType::Bool));
- addResult(ty);
+ addResult(ty, _scope);
return false;
}
void ResolveExpression::thisObject()
{
- if (! _context.symbol())
- return;
-
- Scope *scope = _context.symbol()->scope();
+ Scope *scope = _scope;
for (; scope; scope = scope->enclosingScope()) {
if (scope->isFunctionScope()) {
Function *fun = scope->owner()->asFunction();
Class *klass = cscope->owner()->asClass();
FullySpecifiedType classTy(control()->namedType(klass->name()));
FullySpecifiedType ptrTy(control()->pointerType(classTy));
- addResult(ptrTy, fun);
+ addResult(ptrTy, fun->scope());
break;
} else if (const QualifiedNameId *q = fun->name()->asQualifiedNameId()) {
const Name *nestedNameSpecifier = 0;
nestedNameSpecifier = control()->qualifiedNameId(q->names(), q->nameCount() - 1);
FullySpecifiedType classTy(control()->namedType(nestedNameSpecifier));
FullySpecifiedType ptrTy(control()->pointerType(classTy));
- addResult(ptrTy, fun);
+ addResult(ptrTy, fun->scope());
break;
}
}
FullySpecifiedType charTy = control()->integerType(IntegerType::Char);
charTy.setConst(true);
FullySpecifiedType ty(control()->pointerType(charTy));
- addResult(ty);
+ addResult(ty, _scope);
return false;
}
bool ResolveExpression::visit(QualifiedNameAST *ast)
{
- ResolveClass resolveClass;
- const Name *name = ast->name;
-
- QList<Symbol *> symbols = _context.resolve(name);
- foreach (Symbol *symbol, symbols) {
- if (symbol->isTypedef()) {
- if (NamedType *namedTy = symbol->type()->asNamedType()) {
- const LookupItem r(namedTy, symbol);
- const QList<Symbol *> resolvedClasses =
- resolveClass(namedTy->name(), r, _context);
- if (resolvedClasses.count()) {
- foreach (Symbol *s, resolvedClasses) {
- addResult(s->type(), s);
- }
- continue;
- }
- }
- }
- addResult(symbol->type(), symbol);
+ if (const Name *name = ast->name) {
+ const QList<Symbol *> candidates = _context.lookup(name, _scope);
+ addResults(candidates);
}
- return false;
-}
-bool ResolveExpression::visit(OperatorFunctionIdAST *)
-{
return false;
}
-bool ResolveExpression::visit(ConversionFunctionIdAST *)
+bool ResolveExpression::visit(SimpleNameAST *ast)
{
+ const QList<Symbol *> candidates = _context.lookup(ast->name, _scope);
+ addResults(candidates);
return false;
}
-bool ResolveExpression::visit(SimpleNameAST *ast)
+bool ResolveExpression::visit(TemplateIdAST *ast)
{
- QList<Symbol *> symbols = _context.resolve(ast->name);
- foreach (Symbol *symbol, symbols)
- addResult(symbol->type(), symbol);
-
+ const QList<Symbol *> candidates = _context.lookup(ast->name, _scope);
+ addResults(candidates);
return false;
}
bool ResolveExpression::visit(DestructorNameAST *)
{
FullySpecifiedType ty(control()->voidType());
- addResult(ty);
+ addResult(ty, _scope);
return false;
}
-bool ResolveExpression::visit(TemplateIdAST *ast)
+bool ResolveExpression::visit(OperatorFunctionIdAST *)
{
- QList<Symbol *> symbols = _context.resolve(ast->name);
- foreach (Symbol *symbol, symbols)
- addResult(symbol->type(), symbol);
+ return false;
+}
+bool ResolveExpression::visit(ConversionFunctionIdAST *)
+{
return false;
}
bool ResolveExpression::visit(CallAST *ast)
{
- ResolveClass resolveClass;
-
const QList<LookupItem> baseResults = _results;
_results.clear();
//QList< QList<Result> > arguments;
for (ExpressionListAST *exprIt = ast->expression_list; exprIt; exprIt = exprIt->next) {
- //arguments.append(operator()(exprIt->expression));
+ //arguments.append(resolve(exprIt->expression));
++actualArgumentCount;
}
foreach (const LookupItem &result, baseResults) {
FullySpecifiedType ty = result.type().simplified();
- Symbol *lastVisibleSymbol = result.lastVisibleSymbol();
+ Scope *scope = result.scope();
if (NamedType *namedTy = ty->asNamedType()) {
- const QList<Symbol *> classObjectCandidates = resolveClass(namedTy->name(), result, _context);
-
- foreach (Symbol *classObject, classObjectCandidates) {
- const QList<LookupItem> overloads = resolveMember(functionCallOp, classObject->asClass(), namedTy->name());
-
- foreach (const LookupItem &o, overloads) {
- FullySpecifiedType overloadTy = o.type().simplified();
-
- if (Function *funTy = overloadTy->asFunctionType()) {
- if (maybeValidPrototype(funTy, actualArgumentCount))
- addResult(funTy->returnType().simplified(), lastVisibleSymbol);
+ if (ClassOrNamespace *b = _context.lookupType(namedTy->name(), scope)) {
+ foreach (Symbol *overload, b->find(functionCallOp)) {
+ if (Function *funTy = overload->type()->asFunctionType()) {
+ if (maybeValidPrototype(funTy, actualArgumentCount)) {
+ Function *proto = instantiate(namedTy->name(), funTy)->asFunctionType();
+ addResult(proto->returnType().simplified(), scope);
+ }
}
}
}
} else if (Function *funTy = ty->asFunctionType()) {
if (maybeValidPrototype(funTy, actualArgumentCount))
- addResult(funTy->returnType().simplified(), lastVisibleSymbol);
+ addResult(funTy->returnType().simplified(), scope);
} else if (Class *classTy = ty->asClassType()) {
// Constructor call
FullySpecifiedType ctorTy = control()->namedType(classTy->name());
- addResult(ctorTy, lastVisibleSymbol);
+ addResult(ctorTy, scope);
}
}
const QList<LookupItem> baseResults = _results;
_results.clear();
- const QList<LookupItem> indexResults = operator()(ast->expression);
- ResolveClass resolveClass;
+ const QList<LookupItem> indexResults = resolve(ast->expression);
const Name *arrayAccessOp = control()->operatorNameId(OperatorNameId::ArrayAccessOp);
foreach (const LookupItem &result, baseResults) {
FullySpecifiedType ty = result.type().simplified();
- Symbol *contextSymbol = result.lastVisibleSymbol();
+ Scope *scope = result.scope();
if (PointerType *ptrTy = ty->asPointerType()) {
- addResult(ptrTy->elementType().simplified(), contextSymbol);
+ addResult(ptrTy->elementType().simplified(), scope);
} else if (ArrayType *arrTy = ty->asArrayType()) {
- addResult(arrTy->elementType().simplified(), contextSymbol);
+ addResult(arrTy->elementType().simplified(), scope);
} else if (NamedType *namedTy = ty->asNamedType()) {
- const QList<Symbol *> classObjectCandidates =
- resolveClass(namedTy->name(), result, _context);
-
- foreach (Symbol *classObject, classObjectCandidates) {
- Q_ASSERT(classObject->isClass());
-
- const QList<LookupItem> overloads =
- resolveMember(arrayAccessOp, classObject->asClass(), namedTy->name());
-
- foreach (LookupItem r, overloads) {
- FullySpecifiedType ty = r.type().simplified();
- if (Function *funTy = ty->asFunctionType()) {
- ty = funTy->returnType().simplified();
- addResult(ty, funTy);
+ if (ClassOrNamespace *b = _context.lookupType(namedTy->name(), scope)) {
+ foreach (Symbol *overload, b->find(arrayAccessOp)) {
+ if (Function *funTy = overload->type()->asFunctionType()) {
+ Function *proto = instantiate(namedTy->name(), funTy)->asFunctionType();
+ // ### TODO: check the actual arguments
+ addResult(proto->returnType().simplified(), scope);
}
}
+
}
}
}
{
// The candidate types for the base expression are stored in
// _results.
- QList<LookupItem> baseResults = _results;
+ const QList<LookupItem> baseResults = _results;
+ _results.clear();
// Evaluate the expression-id that follows the access operator.
const Name *memberName = 0;
// Remember the access operator.
const int accessOp = tokenKind(ast->access_token);
- _results = resolveMemberExpression(baseResults, accessOp, memberName);
+ if (ClassOrNamespace *binding = baseExpression(baseResults, accessOp))
+ addResults(binding->lookup(memberName));
return false;
}
-QList<LookupItem>
-ResolveExpression::resolveBaseExpression(const QList<LookupItem> &baseResults, int accessOp,
- bool *replacedDotOperator) const
-{
- QList<LookupItem> results;
-
- if (baseResults.isEmpty())
- return results;
-
- LookupItem result = baseResults.first();
- FullySpecifiedType ty = result.type().simplified();
- Symbol *lastVisibleSymbol = result.lastVisibleSymbol();
-
- if (Function *funTy = ty->asFunctionType()) {
- if (funTy->isAmbiguous())
- ty = funTy->returnType().simplified();
- }
-
- if (accessOp == T_ARROW) {
- if (lastVisibleSymbol && ty->isClassType() && ! lastVisibleSymbol->isClass()) {
- // ### remove ! lastVisibleSymbol->isClass() from the condition.
- results.append(LookupItem(ty, lastVisibleSymbol));
-
- } else if (NamedType *namedTy = ty->asNamedType()) {
- // ### This code is pretty slow.
- const QList<Symbol *> candidates = _context.resolve(namedTy->name());
-
- foreach (Symbol *candidate, candidates) {
- if (candidate->isTypedef()) {
- FullySpecifiedType declTy = candidate->type().simplified();
- const LookupItem r(declTy, candidate);
-
- // update the result
- result = r;
-
- // refresh the cached ty and lastVisibileSymbol.
- ty = result.type().simplified();
- lastVisibleSymbol = result.lastVisibleSymbol();
- break;
- }
- }
- }
-
- if (NamedType *namedTy = ty->asNamedType()) {
- ResolveClass resolveClass;
- const Name *arrowAccessOp = control()->operatorNameId(OperatorNameId::ArrowOp);
- const QList<Symbol *> candidates = resolveClass(namedTy->name(), result, _context);
-
- foreach (Symbol *classObject, candidates) {
- const QList<LookupItem> overloads = resolveMember(arrowAccessOp, classObject->asClass(),
- namedTy->name());
-
- foreach (const LookupItem &r, overloads) {
- FullySpecifiedType typeOfOverloadFunction = r.type().simplified();
- Symbol *lastVisibleSymbol = r.lastVisibleSymbol();
- Function *funTy = typeOfOverloadFunction->asFunctionType();
- if (! funTy)
- continue;
-
- typeOfOverloadFunction = funTy->returnType().simplified();
-
- if (PointerType *ptrTy = typeOfOverloadFunction->asPointerType()) {
- FullySpecifiedType elementTy = ptrTy->elementType().simplified();
-
- if (elementTy->isNamedType())
- results.append(LookupItem(elementTy, lastVisibleSymbol));
- }
- }
- }
- } else if (PointerType *ptrTy = ty->asPointerType()) {
- FullySpecifiedType elementTy = ptrTy->elementType().simplified();
-
- if (elementTy->isNamedType() || elementTy->isClassType())
- results.append(LookupItem(elementTy, lastVisibleSymbol));
- }
- } else if (accessOp == T_DOT) {
- if (replacedDotOperator) {
- if (PointerType *ptrTy = ty->asPointerType()) {
- *replacedDotOperator = true;
- ty = ptrTy->elementType().simplified();
- } else if (ArrayType *arrTy = ty->asArrayType()) {
- *replacedDotOperator = true;
- ty = arrTy->elementType().simplified();
- }
- }
-
- if (NamedType *namedTy = ty->asNamedType()) {
- const QList<Scope *> visibleScopes = _context.visibleScopes(result);
- const QList<Symbol *> candidates = _context.resolve(namedTy->name(), visibleScopes);
- foreach (Symbol *candidate, candidates) {
- if (candidate->isTypedef() && candidate->type()->isNamedType()) {
- ty = candidate->type();
- lastVisibleSymbol = candidate;
- break;
- } else if (TypenameArgument *arg = candidate->asTypenameArgument()) {
- ty = arg->type();
- lastVisibleSymbol = candidate;
- break;
- }
- }
-
- results.append(LookupItem(ty, lastVisibleSymbol));
-
- } else if (Function *fun = ty->asFunctionType()) {
- Scope *funScope = fun->scope();
-
- if (funScope && (funScope->isBlockScope() || funScope->isNamespaceScope())) {
- FullySpecifiedType retTy = fun->returnType().simplified();
- results.append(LookupItem(retTy, lastVisibleSymbol));
- }
- }
- }
-
- return removeDuplicates(results);
-}
-
-QList<LookupItem>
-ResolveExpression::resolveMemberExpression(const QList<LookupItem> &baseResults,
- unsigned accessOp,
- const Name *memberName,
- bool *replacedDotOperator) const
+ClassOrNamespace *ResolveExpression::findClass(const FullySpecifiedType &originalTy, Scope *scope) const
{
- ResolveClass resolveClass;
- QList<LookupItem> results;
+ FullySpecifiedType ty = originalTy.simplified();
+ ClassOrNamespace *binding = 0;
- const QList<LookupItem> classObjectResults = resolveBaseExpression(baseResults, accessOp, replacedDotOperator);
- foreach (const LookupItem &r, classObjectResults) {
- FullySpecifiedType ty = r.type();
+ if (Class *klass = ty->asClassType())
+ binding = _context.lookupType(klass);
- if (Class *klass = ty->asClassType())
- results += resolveMember(memberName, klass);
+ else if (NamedType *namedTy = ty->asNamedType())
+ binding = _context.lookupType(namedTy->name(), scope);
- else if (NamedType *namedTy = ty->asNamedType()) {
- const Name *className = namedTy->name();
- const QList<Symbol *> classes = resolveClass(className, r, _context);
+ else if (Function *funTy = ty->asFunctionType())
+ return findClass(funTy->returnType(), scope);
- foreach (Symbol *c, classes) {
- if (Class *klass = c->asClass())
- results += resolveMember(memberName, klass, className);
- }
- }
- }
-
- return removeDuplicates(results);
+ return binding;
}
-QList<LookupItem>
-ResolveExpression::resolveMember(const Name *memberName, Class *klass,
- const Name *className) const
+ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &baseResults,
+ int accessOp,
+ bool *replacedDotOperator) const
{
- QList<LookupItem> results;
-
- if (! className)
- className = klass->name();
-
- if (! className)
- return results;
-
- QList<Scope *> scopes;
- _context.expand(klass->members(), _context.visibleScopes(), &scopes);
+ foreach (const LookupItem &r, baseResults) {
+ FullySpecifiedType ty = r.type().simplified();
+ Scope *scope = r.scope();
- const QList<Symbol *> candidates = _context.resolve(memberName, scopes);
-
- foreach (Symbol *candidate, candidates) {
- FullySpecifiedType ty = candidate->type();
- const Name *unqualifiedNameId = className;
-
- if (const QualifiedNameId *q = className->asQualifiedNameId())
- unqualifiedNameId = q->unqualifiedNameId();
-
- if (const TemplateNameId *templId = unqualifiedNameId->asTemplateNameId()) {
- GenTemplateInstance::Substitution subst;
-
- for (unsigned i = 0; i < templId->templateArgumentCount(); ++i) {
- FullySpecifiedType templArgTy = templId->templateArgumentAt(i);
-
- if (i < klass->templateParameterCount()) {
- const Name *templArgName = klass->templateParameterAt(i)->name();
- if (templArgName && templArgName->identifier()) {
- const Identifier *templArgId = templArgName->identifier();
- subst.append(qMakePair(templArgId, templArgTy));
+ if (accessOp == T_ARROW) {
+ if (PointerType *ptrTy = ty->asPointerType()) {
+ if (ClassOrNamespace *binding = findClass(ptrTy->elementType(), scope))
+ return binding;
+
+ } else if (ClassOrNamespace *binding = findClass(ty, scope)) {
+ // lookup for overloads of operator->
+ const OperatorNameId *arrowOp = control()->operatorNameId(OperatorNameId::ArrowOp);
+
+ foreach (Symbol *overload, binding->find(arrowOp)) {
+ if (overload->type()->isFunctionType()) {
+ FullySpecifiedType overloadTy = DeprecatedGenTemplateInstance::instantiate(binding->templateId(), overload, control());
+ Function *instantiatedFunction = overloadTy->asFunctionType();
+ Q_ASSERT(instantiatedFunction != 0);
+
+ FullySpecifiedType retTy = instantiatedFunction->returnType().simplified();
+
+ if (PointerType *ptrTy = retTy->asPointerType()) {
+ if (ClassOrNamespace *retBinding = findClass(ptrTy->elementType(), overload->scope()))
+ return retBinding;
+
+ else if (debug) {
+ Overview oo;
+ qDebug() << "no class for:" << oo(ptrTy->elementType());
+ }
+ }
}
}
}
+ } else if (accessOp == T_DOT) {
+ if (replacedDotOperator) {
+ if (PointerType *ptrTy = ty->asPointerType()) {
+ // replace . with ->
+ ty = ptrTy->elementType();
+ *replacedDotOperator = true;
+ }
+ }
- GenTemplateInstance inst(_context, subst);
- ty = inst(candidate);
+ if (ClassOrNamespace *binding = findClass(ty, scope))
+ return binding;
}
-
- results.append(LookupItem(ty, candidate));
}
- return removeDuplicates(results);
+ return 0;
}
-
-QList<LookupItem>
-ResolveExpression::resolveMember(const Name *memberName, ObjCClass *klass) const
+FullySpecifiedType ResolveExpression::instantiate(const Name *className, Symbol *candidate) const
{
- QList<LookupItem> results;
-
- if (!memberName || !klass)
- return results;
-
- QList<Scope *> scopes;
- _context.expand(klass->members(), _context.visibleScopes(), &scopes);
-
- QList<Symbol *> candidates = _context.resolve(memberName, scopes);
-
- foreach (Symbol *candidate, candidates) {
- FullySpecifiedType ty = candidate->type();
-
- results.append(LookupItem(ty, candidate));
- }
-
- return removeDuplicates(results);
+ return DeprecatedGenTemplateInstance::instantiate(className, candidate, _context.control());
}
bool ResolveExpression::visit(PostIncrDecrAST *)
bool ResolveExpression::visit(ObjCMessageExpressionAST *ast)
{
- QList<LookupItem> receiverResults = operator()(ast->receiver_expression);
+ const QList<LookupItem> receiverResults = resolve(ast->receiver_expression);
- if (!receiverResults.isEmpty()) {
- LookupItem result = receiverResults.first();
+ foreach (const LookupItem &result, receiverResults) {
FullySpecifiedType ty = result.type().simplified();
- const Name *klassName = 0;
+ ClassOrNamespace *binding = 0;
- if (const ObjCClass *classTy = ty->asObjCClassType()) {
+ if (ObjCClass *clazz = ty->asObjCClassType()) {
// static access, e.g.:
- // [NSObject description];
- klassName = classTy->name();
- } else if (const PointerType *ptrTy = ty->asPointerType()) {
- const FullySpecifiedType pointeeTy = ptrTy->elementType();
- if (pointeeTy && pointeeTy->isNamedType()) {
+ // [NSObject description];
+ binding = _context.lookupType(clazz);
+ } else if (PointerType *ptrTy = ty->asPointerType()) {
+ if (NamedType *namedTy = ptrTy->asNamedType()) {
// dynamic access, e.g.:
- // NSObject *obj = ...; [obj release];
- klassName = pointeeTy->asNamedType()->name();
+ // NSObject *obj = ...; [obj release];
+ binding = _context.lookupType(namedTy->name(), result.scope());
}
}
- if (klassName&&ast->selector && ast->selector->name) {
- ResolveObjCClass resolveObjCClass;
- QList<Symbol *> resolvedSymbols = resolveObjCClass(klassName, result, _context);
- foreach (Symbol *resolvedSymbol, resolvedSymbols)
- if (ObjCClass *klass = resolvedSymbol->asObjCClass())
- _results.append(resolveMember(ast->selector->name, klass));
- }
+ if (binding)
+ addResults(binding->lookup(ast->selector->name));
}
return false;
}
-////////////////////////////////////////////////////////////////////////////////
-ResolveClass::ResolveClass()
-{ }
-
-QList<Symbol *> ResolveClass::operator()(const Name *name,
- const LookupItem &p,
- const LookupContext &context)
-{
- const QList<LookupItem> previousBlackList = _blackList;
- const QList<Symbol *> symbols = resolveClass(name, p, context);
- _blackList = previousBlackList;
- return symbols;
-}
-
-QList<Symbol *> ResolveClass::resolveClass(const Name *name,
- const LookupItem &p,
- const LookupContext &context)
-{
- QList<Symbol *> resolvedSymbols;
-
- if (_blackList.contains(p))
- return resolvedSymbols;
-
- _blackList.append(p);
-
- const QList<Symbol *> candidates =
- context.resolve(name, context.visibleScopes(p));
-
- foreach (Symbol *candidate, candidates) {
- if (Class *klass = candidate->asClass()) {
- if (resolvedSymbols.contains(klass))
- continue; // we already know about `klass'
- resolvedSymbols.append(klass);
- } else if (candidate->isTypedef()) {
- if (Declaration *decl = candidate->asDeclaration()) {
- if (Class *asClass = decl->type()->asClassType()) {
- // typedef struct { } Point;
- // Point pt;
- // pt.
- resolvedSymbols.append(asClass);
- } else {
- // typedef Point Boh;
- // Boh b;
- // b.
- FullySpecifiedType declType = decl->type().simplified();
- if (NamedType *namedTy = declType->asNamedType()) {
- const LookupItem r(declType, decl);
- resolvedSymbols += resolveClass(namedTy->name(), r, context);
- }
- }
- }
- } else if (Declaration *decl = candidate->asDeclaration()) {
- if (Function *funTy = decl->type()->asFunctionType()) {
- // QString foo("ciao");
- // foo.
- if (funTy->scope() && (funTy->scope()->isBlockScope() || funTy->scope()->isNamespaceScope())) {
- FullySpecifiedType retTy = funTy->returnType().simplified();
- if (NamedType *namedTy = retTy->asNamedType()) {
- const LookupItem r(retTy, decl);
- resolvedSymbols += resolveClass(namedTy->name(), r, context);
- }
- }
- }
- }
- }
-
- return resolvedSymbols;
-}
-
-ResolveObjCClass::ResolveObjCClass()
-{}
-
-QList<Symbol *> ResolveObjCClass::operator ()(const Name *name,
- const LookupItem &p,
- const LookupContext &context)
-{
- QList<Symbol *> resolvedSymbols;
-
- const QList<Symbol *> candidates =
- context.resolve(name, context.visibleScopes(p));
-
- foreach (Symbol *candidate, candidates) {
- if (ObjCClass *klass = candidate->asObjCClass()) {
- if (resolvedSymbols.contains(klass))
- continue; // we already know about `klass'
- resolvedSymbols.append(klass);
- } else if (candidate->isTypedef()) {
- if (Declaration *decl = candidate->asDeclaration()) {
- if (decl->type()->isObjCClassType()) {
- ObjCClass *klass = decl->type()->asObjCClassType();
- if (resolvedSymbols.contains(klass))
- continue;
- resolvedSymbols.append(klass);
- }
- }
- }
- }
-
- return resolvedSymbols;
-}
ResolveExpression(const LookupContext &context);
virtual ~ResolveExpression();
- QList<LookupItem> operator()(ExpressionAST *ast);
+ QList<LookupItem> operator()(ExpressionAST *ast, Scope *scope);
+ QList<LookupItem> resolve(ExpressionAST *ast, Scope *scope);
- QList<LookupItem> resolveMemberExpression(const QList<LookupItem> &baseResults,
- unsigned accessOp,
- const Name *memberName,
- bool *replacedDotOperator = 0) const;
+ ClassOrNamespace *baseExpression(const QList<LookupItem> &baseResults,
+ int accessOp,
+ bool *replacedDotOperator = 0) const;
- QList<LookupItem> resolveBaseExpression(const QList<LookupItem> &baseResults,
- int accessOp,
- bool *replacedDotOperator = 0) const;
-
- QList<LookupItem> resolveMember(const Name *memberName, Class *klass,
- const Name *className = 0) const;
+protected:
+ ClassOrNamespace *findClass(const FullySpecifiedType &ty, Scope *scope) const;
- QList<LookupItem> resolveMember(const Name *memberName, ObjCClass *klass) const;
+ QList<LookupItem> resolve(ExpressionAST *ast);
-protected:
QList<LookupItem> switchResults(const QList<LookupItem> &symbols);
+ FullySpecifiedType instantiate(const Name *className, Symbol *candidate) const;
void thisObject();
- void addResult(const FullySpecifiedType &ty, Symbol *symbol = 0);
- void addResult(const LookupItem &result);
- void addResults(const QList<LookupItem> &results);
+
+ void addResult(const FullySpecifiedType &ty, Scope *scope);
+ void addResults(const QList<Symbol *> &symbols);
bool maybeValidPrototype(Function *funTy, unsigned actualArgumentCount) const;
// Objective-C expressions
virtual bool visit(ObjCMessageExpressionAST *ast);
- QList<Scope *> visibleScopes(const LookupItem &result) const;
-
private:
+ Scope *_scope;
LookupContext _context;
Semantic sem;
QList<LookupItem> _results;
Symbol *_declSymbol;
};
-class CPLUSPLUS_EXPORT ResolveClass
-{
-public:
- ResolveClass();
-
- QList<Symbol *> operator()(const Name *name,
- const LookupItem &p,
- const LookupContext &context);
-
-private:
- QList<Symbol *> resolveClass(const Name *name,
- const LookupItem &p,
- const LookupContext &context);
-
-private:
- QList<LookupItem> _blackList;
-};
-
-class CPLUSPLUS_EXPORT ResolveObjCClass
-{
-public:
- ResolveObjCClass();
-
- QList<Symbol *> operator()(const Name *name,
- const LookupItem &p,
- const LookupContext &context);
-};
-
-
} // end of namespace CPlusPlus
#endif // CPLUSPLUS_RESOLVEEXPRESSION_H
#include "pp.h"
#include <AST.h>
+#include <Symbol.h>
#include <QSet>
using namespace CPlusPlus;
TypeOfExpression::TypeOfExpression():
- m_ast(0)
+ m_ast(0),
+ m_scope(0)
{
}
-Snapshot TypeOfExpression::snapshot() const
+void TypeOfExpression::reset()
{
- return m_snapshot;
+ m_thisDocument.clear();
+ m_snapshot = Snapshot();
+ m_ast = 0;
+ m_scope = 0;
+ m_lookupContext = LookupContext();
+ m_bindings.clear();
+ m_environment.clear();
}
-void TypeOfExpression::setSnapshot(const Snapshot &documents)
+void TypeOfExpression::init(Document::Ptr thisDocument, const Snapshot &snapshot,
+ QSharedPointer<CreateBindings> bindings)
{
- m_snapshot = documents;
+ m_thisDocument = thisDocument;
+ m_snapshot = snapshot;
+ m_ast = 0;
+ m_scope = 0;
m_lookupContext = LookupContext();
+ m_bindings = bindings;
+ m_environment.clear();
}
QList<LookupItem> TypeOfExpression::operator()(const QString &expression,
- Document::Ptr document,
- Symbol *lastVisibleSymbol,
- PreprocessMode mode)
+ Scope *scope,
+ PreprocessMode mode)
{
QString code = expression;
+
if (mode == Preprocess)
- code = preprocessedExpression(expression, m_snapshot, document);
+ code = preprocessedExpression(expression);
+
Document::Ptr expressionDoc = documentForExpression(code);
expressionDoc->check();
m_ast = extractExpressionAST(expressionDoc);
- m_lookupContext = LookupContext(lastVisibleSymbol, expressionDoc,
- document, m_snapshot);
+ m_scope = scope;
- ResolveExpression resolveExpression(m_lookupContext);
- return resolveExpression(m_ast);
+ m_lookupContext = LookupContext(expressionDoc, m_thisDocument, m_snapshot);
+ m_lookupContext.setBindings(m_bindings);
+
+ ResolveExpression resolve(m_lookupContext);
+ return resolve(m_ast, scope);
}
-QString TypeOfExpression::preprocess(const QString &expression,
- Document::Ptr document) const
+QString TypeOfExpression::preprocess(const QString &expression) const
{
- return preprocessedExpression(expression, m_snapshot, document);
+ return preprocessedExpression(expression);
}
ExpressionAST *TypeOfExpression::ast() const
return m_ast;
}
-const LookupContext &TypeOfExpression::lookupContext() const
+Scope *TypeOfExpression::scope() const
+{
+ return m_scope;
+}
+
+const LookupContext &TypeOfExpression::context() const
{
return m_lookupContext;
}
return doc;
}
-void TypeOfExpression::processEnvironment(Snapshot documents,
- Document::Ptr doc, Environment *env,
+void TypeOfExpression::processEnvironment(Document::Ptr doc, Environment *env,
QSet<QString> *processed) const
{
- if (! doc)
- return;
- if (processed->contains(doc->fileName()))
- return;
- processed->insert(doc->fileName());
- foreach (const Document::Include &incl, doc->includes()) {
- processEnvironment(documents,
- documents.document(incl.fileName()),
- env, processed);
+ if (doc && ! processed->contains(doc->fileName())) {
+ processed->insert(doc->fileName());
+
+ foreach (const Document::Include &incl, doc->includes())
+ processEnvironment(m_snapshot.document(incl.fileName()), env, processed);
+
+ foreach (const Macro ¯o, doc->definedMacros())
+ env->bind(macro);
}
- foreach (const Macro ¯o, doc->definedMacros())
- env->bind(macro);
}
-QString TypeOfExpression::preprocessedExpression(const QString &expression,
- Snapshot documents,
- Document::Ptr thisDocument) const
+QString TypeOfExpression::preprocessedExpression(const QString &expression) const
{
if (expression.trimmed().isEmpty())
return expression;
- Environment env;
- QSet<QString> processed;
- processEnvironment(documents, thisDocument,
- &env, &processed);
+ if (! m_environment) {
+ Environment *env = new Environment(); // ### cache the environment.
+
+ QSet<QString> processed;
+ processEnvironment(m_thisDocument, env, &processed);
+ m_environment = QSharedPointer<Environment>(env);
+ }
+
const QByteArray code = expression.toUtf8();
- Preprocessor preproc(0, &env);
+ Preprocessor preproc(0, m_environment.data());
const QByteArray preprocessedCode = preproc("<expression>", code);
return QString::fromUtf8(preprocessedCode.constData(), preprocessedCode.size());
}
#include "CppDocument.h"
#include "LookupContext.h"
+#include "PreprocessorEnvironment.h"
#include <ASTfwd.h>
#include <QtCore/QMap>
class CPLUSPLUS_EXPORT TypeOfExpression
{
+ Q_DISABLE_COPY(TypeOfExpression)
+
public:
TypeOfExpression();
- Snapshot snapshot() const;
-
/**
* Sets the documents used to evaluate expressions. Should be set before
* calling this functor.
* Also clears the lookup context, so can be used to make sure references
* to the documents previously used are removed.
*/
- void setSnapshot(const Snapshot &documents);
+ void init(Document::Ptr thisDocument, const Snapshot &snapshot,
+ QSharedPointer<CreateBindings> bindings = QSharedPointer<CreateBindings>());
+
+ void reset();
enum PreprocessMode {
NoPreprocess,
* has been made!
*
* @param expression The expression to evaluate.
- * @param document The document the expression is part of.
- * @param lastVisibleSymbol The last visible symbol in the document.
+ * @param scope The scope enclosing the expression.
*/
- QList<LookupItem> operator()(const QString &expression, Document::Ptr document,
- Symbol *lastVisibleSymbol,
- PreprocessMode mode = NoPreprocess);
+ QList<LookupItem> operator()(const QString &expression,
+ Scope *scope,
+ PreprocessMode mode = NoPreprocess);
- QString preprocess(const QString &expression, Document::Ptr document) const;
+ QString preprocess(const QString &expression) const;
/**
* Returns the AST of the last evaluated expression.
/**
* Returns the lookup context of the last evaluated expression.
*/
- const LookupContext &lookupContext() const;
+ const LookupContext &context() const;
+ Scope *scope() const;
ExpressionAST *expressionAST() const;
ExpressionAST *extractExpressionAST(Document::Ptr doc) const;
Document::Ptr documentForExpression(const QString &expression) const;
- void processEnvironment(CPlusPlus::Snapshot documents,
- CPlusPlus::Document::Ptr doc, CPlusPlus::Environment *env,
+ void processEnvironment(Document::Ptr doc, Environment *env,
QSet<QString> *processed) const;
- QString preprocessedExpression(const QString &expression,
- CPlusPlus::Snapshot documents,
- CPlusPlus::Document::Ptr thisDocument) const;
+ QString preprocessedExpression(const QString &expression) const;
+private:
+ Document::Ptr m_thisDocument;
Snapshot m_snapshot;
+ QSharedPointer<CreateBindings> m_bindings;
ExpressionAST *m_ast;
+ Scope *m_scope;
LookupContext m_lookupContext;
+ mutable QSharedPointer<Environment> m_environment;
};
} // namespace CPlusPlus
$$PWD/TypeOfExpression.h \
$$PWD/TypePrettyPrinter.h \
$$PWD/ResolveExpression.h \
+ $$PWD/LookupItem.h \
$$PWD/LookupContext.h \
+ $$PWD/DeprecatedLookupContext.h \
$$PWD/CppBindings.h \
$$PWD/ASTParent.h \
- $$PWD/GenTemplateInstance.h \
+ $$PWD/DeprecatedGenTemplateInstance.h \
$$PWD/FindUsages.h \
$$PWD/CheckUndefinedSymbols.h \
$$PWD/DependencyTable.h \
$$PWD/TypeOfExpression.cpp \
$$PWD/TypePrettyPrinter.cpp \
$$PWD/ResolveExpression.cpp \
+ $$PWD/LookupItem.cpp \
$$PWD/LookupContext.cpp \
+ $$PWD/DeprecatedLookupContext.cpp \
$$PWD/CppBindings.cpp \
$$PWD/ASTParent.cpp \
- $$PWD/GenTemplateInstance.cpp \
+ $$PWD/DeprecatedGenTemplateInstance.cpp \
$$PWD/FindUsages.cpp \
$$PWD/CheckUndefinedSymbols.cpp \
$$PWD/DependencyTable.cpp \
/* A messagebox suitable for questions with a
* "Do not ask me again" checkbox. Emulates the QMessageBox API with
- * static conveniences. */
+ * static conveniences. The message label can open external URLs. */
class QTCREATOR_UTILS_EXPORT CheckableMessageBox : public QDialog
{
<ui version="4.0">
<class>Utils::CheckableMessageBox</class>
<widget class="QDialog" name="Utils::CheckableMessageBox">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>195</width>
- <height>107</height>
- </rect>
- </property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
- <widget class="QLabel" name="pixmapLabel"/>
+ <widget class="QLabel" name="pixmapLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
</item>
<item>
<spacer name="pixmapSpacer">
</item>
<item>
<widget class="QLabel" name="messageLabel">
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
- <string>TextLabel</string>
+ <string notr="true">dummyText</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox">
<property name="text">
- <string>CheckBox</string>
+ <string notr="true">CheckBox</string>
</property>
</widget>
</item>
**************************************************************************/
#include "uncommentselection.h"
+#include <QtCore/QtGlobal>
#include <QtGui/QPlainTextEdit>
#include <QtGui/QTextCursor>
#include <QtGui/QTextBlock>
#include <QtGui/QTextDocument>
-void Utils::unCommentSelection(QPlainTextEdit *edit)
+using namespace Utils;
+
+CommentDefinition::CommentDefinition() :
+ m_afterWhiteSpaces(false),
+ m_singleLine(QLatin1String("//")),
+ m_multiLineStart(QLatin1String("/*")),
+ m_multiLineEnd(QLatin1String("*/"))
+{}
+
+CommentDefinition &CommentDefinition::setAfterWhiteSpaces(const bool afterWhiteSpaces)
+{
+ m_afterWhiteSpaces = afterWhiteSpaces;
+ return *this;
+}
+
+CommentDefinition &CommentDefinition::setSingleLine(const QString &singleLine)
+{
+ m_singleLine = singleLine;
+ return *this;
+}
+
+CommentDefinition &CommentDefinition::setMultiLineStart(const QString &multiLineStart)
+{
+ m_multiLineStart = multiLineStart;
+ return *this;
+}
+
+CommentDefinition &CommentDefinition::setMultiLineEnd(const QString &multiLineEnd)
{
+ m_multiLineEnd = multiLineEnd;
+ return *this;
+}
+
+bool CommentDefinition::isAfterWhiteSpaces() const
+{ return m_afterWhiteSpaces; }
+
+const QString &CommentDefinition::singleLine() const
+{ return m_singleLine; }
+
+const QString &CommentDefinition::multiLineStart() const
+{ return m_multiLineStart; }
+
+const QString &CommentDefinition::multiLineEnd() const
+{ return m_multiLineEnd; }
+
+bool CommentDefinition::hasSingleLineStyle() const
+{ return !m_singleLine.isEmpty(); }
+
+bool CommentDefinition::hasMultiLineStyle() const
+{ return !m_multiLineStart.isEmpty() && !m_multiLineEnd.isEmpty(); }
+
+void CommentDefinition::clearCommentStyles()
+{
+ m_singleLine.clear();
+ m_multiLineStart.clear();
+ m_multiLineEnd.clear();
+}
+
+namespace {
+
+bool isComment(const QString &text,
+ int index,
+ const CommentDefinition &definition,
+ const QString & (CommentDefinition::* comment) () const)
+{
+ const QString &commentType = ((definition).*(comment))();
+ const int length = commentType.length();
+
+ Q_ASSERT(text.length() - index >= length);
+
+ int i = 0;
+ while (i < length) {
+ if (text.at(index + i) != commentType.at(i))
+ return false;
+ ++i;
+ }
+ return true;
+}
+
+} // namespace anynomous
+
+
+void Utils::unCommentSelection(QPlainTextEdit *edit, const CommentDefinition &definition)
+{
+ if (!definition.hasSingleLineStyle() && !definition.hasMultiLineStyle())
+ return;
+
QTextCursor cursor = edit->textCursor();
QTextDocument *doc = cursor.document();
cursor.beginEditBlock();
endBlock = endBlock.previous();
}
- bool doCStyleUncomment = false;
- bool doCStyleComment = false;
- bool doCppStyleUncomment = false;
+ bool doMultiLineStyleUncomment = false;
+ bool doMultiLineStyleComment = false;
+ bool doSingleLineStyleUncomment = false;
bool hasSelection = cursor.hasSelection();
- if (hasSelection) {
+ if (hasSelection && definition.hasMultiLineStyle()) {
+
QString startText = startBlock.text();
int startPos = start - startBlock.position();
+ const int multiLineStartLength = definition.multiLineStart().length();
bool hasLeadingCharacters = !startText.left(startPos).trimmed().isEmpty();
- if ((startPos >= 2
- && startText.at(startPos-2) == QLatin1Char('/')
- && startText.at(startPos-1) == QLatin1Char('*'))) {
- startPos -= 2;
- start -= 2;
- }
- bool hasSelStart = (startPos < startText.length() - 1
- && startText.at(startPos) == QLatin1Char('/')
- && startText.at(startPos+1) == QLatin1Char('*'));
+ if (startPos >= multiLineStartLength
+ && isComment(startText,
+ startPos - multiLineStartLength,
+ definition,
+ &CommentDefinition::multiLineStart)) {
+ startPos -= multiLineStartLength;
+ start -= multiLineStartLength;
+ }
+ bool hasSelStart = (startPos <= startText.length() - multiLineStartLength
+ && isComment(startText,
+ startPos,
+ definition,
+ &CommentDefinition::multiLineStart));
QString endText = endBlock.text();
int endPos = end - endBlock.position();
- bool hasTrailingCharacters = !endText.left(endPos).remove(QLatin1String("//")).trimmed().isEmpty()
- && !endText.mid(endPos).trimmed().isEmpty();
- if ((endPos <= endText.length() - 2
- && endText.at(endPos) == QLatin1Char('*')
- && endText.at(endPos+1) == QLatin1Char('/'))) {
- endPos += 2;
- end += 2;
+ const int multiLineEndLength = definition.multiLineEnd().length();
+ bool hasTrailingCharacters =
+ !endText.left(endPos).remove(definition.singleLine()).trimmed().isEmpty()
+ && !endText.mid(endPos).trimmed().isEmpty();
+
+ if (endPos <= endText.length() - multiLineEndLength
+ && isComment(endText, endPos, definition, &CommentDefinition::multiLineEnd)) {
+ endPos += multiLineEndLength;
+ end += multiLineEndLength;
}
- bool hasSelEnd = (endPos >= 2
- && endText.at(endPos-2) == QLatin1Char('*')
- && endText.at(endPos-1) == QLatin1Char('/'));
+ bool hasSelEnd = (endPos >= multiLineEndLength
+ && isComment(endText,
+ endPos - multiLineEndLength,
+ definition,
+ &CommentDefinition::multiLineEnd));
+
+ doMultiLineStyleUncomment = hasSelStart && hasSelEnd;
+ doMultiLineStyleComment = !doMultiLineStyleUncomment
+ && (hasLeadingCharacters
+ || hasTrailingCharacters
+ || !definition.hasSingleLineStyle());
+ } else if (!hasSelection && !definition.hasSingleLineStyle()) {
- doCStyleUncomment = hasSelStart && hasSelEnd;
- doCStyleComment = !doCStyleUncomment && (hasLeadingCharacters || hasTrailingCharacters);
+ QString text = startBlock.text().trimmed();
+ doMultiLineStyleUncomment = text.startsWith(definition.multiLineStart())
+ && text.endsWith(definition.multiLineEnd());
+ doMultiLineStyleComment = !doMultiLineStyleUncomment && !text.isEmpty();
+
+ start = startBlock.position();
+ end = endBlock.position() + endBlock.length() - 1;
+
+ if (doMultiLineStyleUncomment) {
+ int offset = 0;
+ text = startBlock.text();
+ const int length = text.length();
+ while (offset < length && text.at(offset).isSpace())
+ ++offset;
+ start += offset;
+ }
}
- if (doCStyleUncomment) {
+ if (doMultiLineStyleUncomment) {
cursor.setPosition(end);
- cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, 2);
+ cursor.movePosition(QTextCursor::PreviousCharacter,
+ QTextCursor::KeepAnchor,
+ definition.multiLineEnd().length());
cursor.removeSelectedText();
cursor.setPosition(start);
- cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
+ cursor.movePosition(QTextCursor::NextCharacter,
+ QTextCursor::KeepAnchor,
+ definition.multiLineStart().length());
cursor.removeSelectedText();
- } else if (doCStyleComment) {
+ } else if (doMultiLineStyleComment) {
cursor.setPosition(end);
- cursor.insertText(QLatin1String("*/"));
+ cursor.insertText(definition.multiLineEnd());
cursor.setPosition(start);
- cursor.insertText(QLatin1String("/*"));
+ cursor.insertText(definition.multiLineStart());
} else {
endBlock = endBlock.next();
- doCppStyleUncomment = true;
+ doSingleLineStyleUncomment = true;
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
QString text = block.text().trimmed();
- if (!text.isEmpty() && !text.startsWith(QLatin1String("//"))) {
- doCppStyleUncomment = false;
+ if (!text.isEmpty() && !text.startsWith(definition.singleLine())) {
+ doSingleLineStyleUncomment = false;
break;
}
}
+
+ const int singleLineLength = definition.singleLine().length();
for (QTextBlock block = startBlock; block != endBlock; block = block.next()) {
- if (doCppStyleUncomment) {
+ if (doSingleLineStyleUncomment) {
QString text = block.text();
int i = 0;
- while (i < text.size() - 1) {
- if (text.at(i) == QLatin1Char('/')
- && text.at(i + 1) == QLatin1Char('/')) {
+ while (i <= text.size() - singleLineLength) {
+ if (isComment(text, i, definition, &CommentDefinition::singleLine)) {
cursor.setPosition(block.position() + i);
- cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
+ cursor.movePosition(QTextCursor::NextCharacter,
+ QTextCursor::KeepAnchor,
+ singleLineLength);
cursor.removeSelectedText();
break;
}
QString text = block.text();
foreach(QChar c, text) {
if (!c.isSpace()) {
- cursor.setPosition(block.position());
- cursor.insertText(QLatin1String("//"));
+ if (definition.isAfterWhiteSpaces())
+ cursor.setPosition(block.position() + text.indexOf(c));
+ else
+ cursor.setPosition(block.position());
+ cursor.insertText(definition.singleLine());
break;
}
}
}
// adjust selection when commenting out
- if (hasSelection && !doCStyleUncomment && !doCppStyleUncomment) {
+ if (hasSelection && !doMultiLineStyleUncomment && !doSingleLineStyleUncomment) {
cursor = edit->textCursor();
- if (!doCStyleComment)
- start = startBlock.position(); // move the double slashes into the selection
+ if (!doMultiLineStyleComment)
+ start = startBlock.position(); // move the comment into the selection
int lastSelPos = anchorIsStart ? cursor.position() : cursor.anchor();
if (anchorIsStart) {
cursor.setPosition(start);
cursor.endEditBlock();
}
-
#include "utils_global.h"
+#include <QtCore/QString>
+
QT_BEGIN_NAMESPACE
class QPlainTextEdit;
QT_END_NAMESPACE
namespace Utils {
-QTCREATOR_UTILS_EXPORT void unCommentSelection(QPlainTextEdit *edit);
+class QTCREATOR_UTILS_EXPORT CommentDefinition
+{
+public:
+ CommentDefinition();
+
+ CommentDefinition &setAfterWhiteSpaces(const bool);
+ CommentDefinition &setSingleLine(const QString &singleLine);
+ CommentDefinition &setMultiLineStart(const QString &multiLineStart);
+ CommentDefinition &setMultiLineEnd(const QString &multiLineEnd);
+
+ bool isAfterWhiteSpaces() const;
+ const QString &singleLine() const;
+ const QString &multiLineStart() const;
+ const QString &multiLineEnd() const;
+
+ bool hasSingleLineStyle() const;
+ bool hasMultiLineStyle() const;
+
+ void clearCommentStyles();
+
+private:
+ bool m_afterWhiteSpaces;
+ QString m_singleLine;
+ QString m_multiLineStart;
+ QString m_multiLineEnd;
+};
+
+QTCREATOR_UTILS_EXPORT
+void unCommentSelection(QPlainTextEdit *edit,
+ const CommentDefinition &definiton = CommentDefinition());
} // end of namespace Utils
-<plugin name="BinEditor" version="1.3.84" compatVersion="1.3.84">
+<plugin name="BinEditor" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Binary editor component.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
</dependencyList>
</plugin>
m_baseAddr = static_cast<quint64>(range/2) > startAddr
? 0 : startAddr - range/2;
m_baseAddr = (m_baseAddr / blockSize) * blockSize;
- m_size = m_baseAddr != 0 && static_cast<quint64>(range) >= -m_baseAddr
- ? -m_baseAddr : range;
+ const quint64 maxRange = Q_UINT64_C(0xffffffffffffffff) - m_baseAddr + 1;
+ m_size = m_baseAddr != 0 && static_cast<quint64>(range) >= maxRange
+ ? maxRange : range;
m_addressBytes = (m_baseAddr + m_size < quint64(1) << 32
&& m_baseAddr + m_size >= m_baseAddr) ? 4 : 8;
}
}
+ void rename(const QString &newName) {
+ m_fileName = newName;
+ m_editor->editorInterface()->setDisplayName(QFileInfo(fileName()).fileName());
+ emit changed();
+ }
+
bool open(const QString &fileName) {
QFile file(fileName);
if (file.open(QIODevice::ReadOnly)) {
m_toolBar->addWidget(w);
connect(m_editor, SIGNAL(cursorPositionChanged(int)), this, SLOT(updateCursorPosition(int)));
+ connect(m_file, SIGNAL(changed()), this, SIGNAL(changed()));
}
~BinEditorInterface() {
delete m_editor;
layout->setMargin(0);
m_imageView->setLayout(layout);
layout->addWidget(m_label, 0, 0, 1, 1);
+
+ connect(m_file, SIGNAL(changed()),
+ this, SIGNAL(changed()));
}
ImageViewer::~ImageViewer()
explicit ImageViewerFile(ImageViewer *parent = 0);
bool save(const QString &fileName = QString()) { Q_UNUSED(fileName); return false; }
+ void rename(const QString &newName) { m_fileName = newName; }
QString fileName() const { return m_fileName; }
QString defaultPath() const { return QString(); }
-<plugin name="Bookmarks" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Bookmarks" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Bookmarks in text editors.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
-<plugin name="CMakeProjectManager" version="1.3.84" compatVersion="1.3.84">
+<plugin name="CMakeProjectManager" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>CMake support</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="CppTools" version="1.3.84"/>
- <dependency name="CppEditor" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="CppTools" version="2.0.80"/>
+ <dependency name="CppEditor" version="2.0.80"/>
</dependencyList>
</plugin>
return false;
}
+void CMakeFile::rename(const QString &newName)
+{
+ Q_ASSERT(false);
+ Q_UNUSED(newName);
+ // Can't happen....
+}
+
Core::IFile::ReloadBehavior CMakeFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
Q_UNUSED(state)
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
void reload(ReloadFlag flag, ChangeType type);
+ void rename(const QString &newName);
+
private:
CMakeProject *m_project;
QString m_fileName;
return true;
}
-QList<ProjectExplorer::ProjectNode::ProjectAction> CMakeProjectNode::supportedActions() const
+QList<ProjectExplorer::ProjectNode::ProjectAction> CMakeProjectNode::supportedActions(Node *node) const
{
+ Q_UNUSED(node);
return QList<ProjectAction>();
}
public:
CMakeProjectNode(const QString &fileName);
virtual bool hasBuildTargets() const;
- virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions() const;
+ virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions(Node *node) const;
virtual bool addSubProjects(const QStringList &proFilePaths);
virtual bool removeSubProjects(const QStringList &proFilePaths);
virtual bool addFiles(const ProjectExplorer::FileType fileType,
-<plugin name="Core" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Core" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
namespace Core {
namespace Constants {
-#define IDE_VERSION_MAJOR 1
-#define IDE_VERSION_MINOR 3
-#define IDE_VERSION_RELEASE 84
+#define IDE_VERSION_MAJOR 2
+#define IDE_VERSION_MINOR 0
+#define IDE_VERSION_RELEASE 80
#define STRINGIFY_INTERNAL(x) #x
#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
{
m_statusHLine->setFrameStyle(QFrame::HLine);
- m_statusWidget->setFrameStyle(QFrame::Panel | QFrame::Raised);
- m_statusWidget->setLineWidth(1);
+ m_statusWidget->setFrameStyle(QFrame::NoFrame);
+ m_statusWidget->setLineWidth(0);
//m_statusWidget->setForegroundRole(QPalette::ToolTipText);
//m_statusWidget->setBackgroundRole(QPalette::ToolTipBase);
m_statusWidget->setAutoFillBackground(true);
-
QHBoxLayout *hbox = new QHBoxLayout(m_statusWidget);
- hbox->setMargin(2);
- m_statusWidgetLabel = new QLabel("Placeholder");
+ hbox->setContentsMargins(1, 0, 1, 1);
+ m_statusWidgetLabel = new QLabel;
m_statusWidgetLabel->setForegroundRole(QPalette::ToolTipText);
+ m_statusWidgetLabel->setContentsMargins(3, 0, 3, 0);
hbox->addWidget(m_statusWidgetLabel);
hbox->addStretch(1);
m_statusWidgetButton = new QToolButton;
- m_statusWidgetButton->setText(tr("Placeholder"));
+ m_statusWidgetButton->setContentsMargins(0, 0, 0, 0);
+ //m_statusWidgetButton->setIcon(QIcon(":/core/images/clear.png"));
hbox->addWidget(m_statusWidgetButton);
m_statusHLine->setVisible(false);
m_statusWidgetId = id;
m_statusWidgetLabel->setText(infoText);
m_statusWidgetButton->setText(buttonText);
+ m_statusWidgetButton->setToolTip(buttonText);
m_statusWidgetButton->disconnect();
if (object && member)
connect(m_statusWidgetButton, SIGNAL(clicked()), object, member);
QString m_lastVisitedDirectory;
QString m_projectsDirectory;
bool m_useProjectsDirectory;
+ // When we are callling into a IFile
+ // we don't want to receive a changed()
+ // signal
+ // That makes the code easier
+ IFile *m_blockedIFile;
};
FileManagerPrivate::FileManagerPrivate(QObject *q, QMainWindow *mw) :
m_blockActivated(false),
m_lastVisitedDirectory(QDir::currentPath()),
#ifdef Q_OS_MAC // Creator is in bizarre places when launched via finder.
- m_useProjectsDirectory(true)
+ m_useProjectsDirectory(true),
#else
- m_useProjectsDirectory(false)
+ m_useProjectsDirectory(false),
#endif
+ m_blockedIFile(0)
{
}
d->m_states[fixedname].lastUpdatedState.insert(file, item);
}
+/// Dumps the state of the file manager's map
+/// For debugging purposes
+void FileManager::dump()
+{
+ QMap<QString, Internal::FileState>::const_iterator it, end;
+ it = d->m_states.constBegin();
+ end = d->m_states.constEnd();
+ for (; it != end; ++it) {
+ qDebug()<<" ";
+ qDebug() << it.key();
+ qDebug() << it.value().expected.modified;
+
+ QMap<IFile *, Internal::FileStateItem>::const_iterator jt, jend;
+ jt = it.value().lastUpdatedState.constBegin();
+ jend = it.value().lastUpdatedState.constEnd();
+ for (; jt != jend; ++jt) {
+ qDebug() << jt.key() << jt.value().modified;
+ }
+ }
+}
+
+void FileManager::renamedFile(const QString &from, QString &to)
+{
+ QString fixedFrom = fixFileName(from);
+ QString fixedTo = fixFileName(to);
+ if (d->m_states.contains(fixedFrom)) {
+ QTC_ASSERT(!d->m_states.contains(to), return);
+ d->m_states.insert(fixedTo, d->m_states.value(fixedFrom));
+ d->m_states.remove(fixedFrom);
+ QFileInfo fi(to);
+ d->m_states[fixedTo].expected.modified = fi.lastModified();
+ d->m_states[fixedTo].expected.permissions = fi.permissions();
+
+ d->m_fileWatcher->removePath(fixedFrom);
+ d->m_fileWatcher->addPath(fixedTo);
+
+ QMap<IFile *, Internal::FileStateItem>::iterator it, end;
+ it = d->m_states[fixedTo].lastUpdatedState.begin();
+ end = d->m_states[fixedTo].lastUpdatedState.end();
+
+ for ( ; it != end; ++it) {
+ d->m_blockedIFile = it.key();
+ it.key()->rename(to);
+ d->m_blockedIFile = it.key();
+ it.value().modified = fi.lastModified();
+ }
+ }
+}
+
///
/// Does not use file->fileName, as such is save to use
/// with renamed files and deleted files
void FileManager::checkForNewFileName()
{
IFile *file = qobject_cast<IFile *>(sender());
+ // We modified the IFile
+ // Trust the other code to also update the m_states map
+ if (file == d->m_blockedIFile)
+ return;
QTC_ASSERT(file, return);
const QString &fileName = fixFileName(file->fileName());
end = lastUpdated.constEnd();
for ( ; it != end; ++it) {
IFile *file = it.key();
+ d->m_blockedIFile = file;
// Compare
if (it.value().modified == fi.lastModified()
&& it.value().permissions == fi.permissions()) {
}
updateFileInfo(file);
+ d->m_blockedIFile = 0;
}
}
bool isFileManaged(const QString &fileName) const;
QList<IFile *> modifiedFiles() const;
+ void renamedFile(const QString &from, QString &to);
+
void blockFileChange(IFile *file);
void unblockFileChange(IFile *file);
QStringList recentFiles() const;
void saveRecentFiles();
+
// current file
void setCurrentFile(const QString &filePath);
QString currentFile() const;
void syncWithEditor(Core::IContext *context);
private:
+ void dump();
void addFileInfo(IFile *file);
void removeFileInfo(IFile *file);
void removeFileInfo(const QString &fileName, IFile *file);
virtual ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const = 0;
virtual void reload(ReloadFlag flag, ChangeType type) = 0;
+ virtual void rename(const QString &newName) = 0;
virtual void checkPermissions() {}
Q_OBJECT
public:
enum Operation {
- AddOperation, DeleteOperation, OpenOperation,
+ AddOperation, DeleteOperation, OpenOperation, MoveOperation,
CreateRepositoryOperation,
SnapshotOperations,
AnnotateOperation
/*!
* Returns whether files in this directory should be managed with this
* version control.
+ * If \a topLevel is non-null, it should return the topmost directory,
+ * for which this IVersionControl should be used. The VCSManager assumes
+ * that all files in the returned directory are managed by the same IVersionControl.
*/
- virtual bool managesDirectory(const QString &filename) const = 0;
- /*!
- * This function should return the topmost directory, for which this
- * IVersionControl should be used. The VCSManager assumes that all files in
- * the returned directory are managed by the same IVersionControl.
- *
- * Note that this is used as an optimization, so that the VCSManager
- * doesn't need to call managesDirectory(..) for each directory.
- *
- * This function is called after finding out that the directory is managed
- * by a specific version control.
- */
- virtual QString findTopLevelForDirectory(const QString &directory) const = 0;
+ virtual bool managesDirectory(const QString &filename, QString *topLevel = 0) const = 0;
/*!
* Called to query whether a VCS supports the respective operations.
virtual bool vcsDelete(const QString &filename) = 0;
/*!
+ * Called to rename a file, should do the actual on disk renaming
+ * (e.g. git mv, svn move, p4 move)
+ */
+ virtual bool vcsMove(const QString &from, const QString &to) = 0;
+
+ /*!
* Called to initialize the version control system in a directory.
*/
virtual bool vcsCreateRepository(const QString &directory) = 0;
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
+#include <QtCore/QMutex>
#include <QtCore/QThread>
+#include <QtCore/QWaitCondition>
#include <ne7ssh.h>
quit();
}
- bool start(bool shell)
+ bool start(bool shell, void (*callbackFunc)(void *), void *callbackArg)
{
Q_ASSERT(m_channel == -1);
try {
const QString *authString;
int (ne7ssh::*connFunc)(const char *, int, const char *,
- const char *, bool, int);
+ const char *, bool, int, void (*)(void *), void *);
if (m_server.authType == SshServerInfo::AuthByPwd) {
authString = &m_server.pwd;
connFunc = &ne7ssh::connectWithPassword;
connFunc = &ne7ssh::connectWithKey;
}
m_channel = (ssh.data()->*connFunc)(m_server.host.toLatin1(),
- m_server.port, m_server.uname.toAscii(),
- authString->toLatin1(), shell, m_server.timeout);
+ m_server.port, m_server.uname.toAscii(), authString->toLatin1(),
+ shell, m_server.timeout, callbackFunc, callbackArg);
if (m_channel == -1) {
setError(tr("Could not connect to host."), false);
return false;
}
} catch (const std::exception &e) {
- // Should in theory not be necessary, but Net7 leaks Botan exceptions.
+ // Should in theory not be necessary, but Net7 leaks Botan exceptions.
setError(tr("Error in cryptography backend: %1")
.arg(QLatin1String(e.what())), false);
return false;
}
}
+ bool isConnected() const { return channel() != -1; }
bool hasError() const { return !m_error.isEmpty(); }
QString error() const { return m_error; }
int channel() const { return m_channel; }
int m_channel;
};
-
char *alloc(size_t n)
{
return new char[n];
GenericSshConnection conn;
ConnectionOutputReader *outputReader;
+ QByteArray remoteOutput;
+ QMutex mutex;
+ QWaitCondition waitCond;
};
struct NonInteractiveSshConnectionPrivate
{
public:
ConnectionOutputReader(InteractiveSshConnection *parent)
- : QThread(parent), m_conn(parent), m_stopRequested(false)
+ : QThread(parent), m_conn(parent), m_stopRequested(false),
+ m_dataAvailable(false)
{}
~ConnectionOutputReader()
wait();
}
- // TODO: Use a wakeup mechanism here as soon as we no longer poll for output
- // from Net7.
void stop()
{
+ m_mutex.lock();
m_stopRequested = true;
+ m_waitCond.wakeOne();
+ m_mutex.unlock();
+ }
+
+ void dataAvailable()
+ {
+ m_mutex.lock();
+ m_dataAvailable = true;
+ m_waitCond.wakeOne();
+ m_mutex.unlock();
}
private:
virtual void run()
{
- while (!m_stopRequested) {
+ while (true) {
+ m_mutex.lock();
+ if (m_stopRequested) {
+ m_mutex.unlock();
+ return;
+ }
const int channel = m_conn->d->conn.channel();
- if (channel != -1) {
- QScopedPointer<char, QScopedPointerArrayDeleter<char> >
+ if (!m_dataAvailable || channel == -1)
+ m_waitCond.wait(&m_mutex);
+ m_dataAvailable = false;
+ m_mutex.unlock();
+ QScopedPointer<char, QScopedPointerArrayDeleter<char> >
output(m_conn->d->conn.ssh->readAndReset(channel, alloc));
- if (output)
- emit m_conn->remoteOutput(QByteArray(output.data()));
+ if (output) {
+ m_conn->d->mutex.lock();
+ m_conn->d->remoteOutput += output.data();
+ emit m_conn->remoteOutputAvailable();
+ m_conn->d->mutex.unlock();
}
- usleep(100000); // TODO: Hack Net7 to enable wait() functionality.
}
}
InteractiveSshConnection *m_conn;
bool m_stopRequested;
+ bool m_dataAvailable;
+ QMutex m_mutex;
+ QWaitCondition m_waitCond;
};
} // namespace Internal
+namespace {
+
+void wakeupReader(void *opaqueReader)
+{
+ static_cast<Internal::ConnectionOutputReader*>(opaqueReader)->dataAvailable();
+}
+
+} // Anonymous namespace
+
+
InteractiveSshConnection::InteractiveSshConnection(const SshServerInfo &server)
: d(new Internal::InteractiveSshConnectionPrivate(server))
{
bool InteractiveSshConnection::start()
{
- if (!d->conn.start(true))
+ if (isConnected())
+ return true;
+
+ if (!d->conn.start(true, wakeupReader, d->outputReader))
return false;
d->outputReader->start();
void InteractiveSshConnection::quit()
{
+ d->mutex.lock();
+ d->waitCond.wakeOne();
+ d->mutex.unlock();
d->outputReader->stop();
d->conn.quit();
}
+QByteArray InteractiveSshConnection::waitForRemoteOutput(int msecs)
+{
+ d->mutex.lock();
+ if (d->remoteOutput.isEmpty())
+ d->waitCond.wait(&d->mutex, msecs == -1 ? ULONG_MAX : msecs);
+ const QByteArray remoteOutput = d->remoteOutput;
+ d->remoteOutput.clear();
+ d->mutex.unlock();
+ return remoteOutput;
+}
+
+
InteractiveSshConnection::Ptr InteractiveSshConnection::create(const SshServerInfo &server)
{
return Ptr(new InteractiveSshConnection(server));
}
+bool InteractiveSshConnection::isConnected() const
+{
+ return d->conn.isConnected();
+}
+
bool InteractiveSshConnection::hasError() const
{
return d->conn.hasError();
bool SftpConnection::start()
{
- if (!d->conn.start(false))
+ if (isConnected())
+ return true;
+ if (!d->conn.start(false, 0, 0))
return false;
if (!d->conn.ssh->initSftp(d->sftp, d->conn.channel())
|| !d->sftp.setTimeout(d->conn.server().timeout)) {
d->conn.setError(tr("Error setting up SFTP subsystem"), true);
+ quit();
return false;
}
return true;
d->conn.quit();
}
+bool SftpConnection::isConnected() const
+{
+ return d->conn.isConnected();
+}
+
bool SftpConnection::hasError() const
{
return d->conn.hasError();
bool start();
void quit();
+ bool isConnected() const;
bool sendInput(const QByteArray &input); // Should normally end in newline.
+ QByteArray waitForRemoteOutput(int msecs = -1);
bool hasError() const;
QString error() const;
~InteractiveSshConnection();
signals:
- void remoteOutput(const QByteArray &output);
+ void remoteOutputAvailable();
private:
InteractiveSshConnection(const SshServerInfo &server);
static Ptr create(const SshServerInfo &server);
bool start();
void quit();
+ bool isConnected() const;
bool hasError() const;
QString error() const;
bool upload(const QString &localFilePath, const QByteArray &remoteFilePath);
QString *topLevelDirectory)
{
typedef VersionControlCache::const_iterator VersionControlCacheConstIterator;
+
+ if (debug) {
+ qDebug(">findVersionControlForDirectory %s topLevelPtr %d",
+ qPrintable(directory), (topLevelDirectory != 0));
+ if (debug > 1) {
+ const VersionControlCacheConstIterator cend = m_d->m_cachedMatches.constEnd();
+ for (VersionControlCacheConstIterator it = m_d->m_cachedMatches.constBegin(); it != cend; ++it)
+ qDebug("Cache %s -> '%s'", qPrintable(it.key()), qPrintable(it.value()->displayName()));
+ }
+ }
+ QTC_ASSERT(!directory.isEmpty(), return 0);
+
const VersionControlCacheConstIterator cacheEnd = m_d->m_cachedMatches.constEnd();
if (topLevelDirectory)
if (fullPathIt != cacheEnd) {
if (topLevelDirectory)
*topLevelDirectory = directory;
+ if (debug)
+ qDebug("<findVersionControlForDirectory: full cache match for VCS '%s'", qPrintable(fullPathIt.value()->displayName()));
return fullPathIt.value();
}
- // Split the path, starting from top, try to find the matching repository
- int pos = 0;
+ // Split the path, trying to find the matching repository. We start from the reverse
+ // in order to detected nested repositories correctly (say, a git checkout under SVN).
+ // Note that detection of a nested version control will still fail if the
+ // above-located version control is detected and entered into the cache first.
+ // The nested one can then no longer be found due to the splitting of the paths.
+ int pos = directory.size() - 1;
const QChar slash = QLatin1Char('/');
while (true) {
- const int index = directory.indexOf(slash, pos);
- if (index == -1)
+ const int index = directory.lastIndexOf(slash, pos);
+ if (index <= 0) // Stop at '/' or not found
break;
const QString directoryPart = directory.left(index);
const VersionControlCacheConstIterator it = m_d->m_cachedMatches.constFind(directoryPart);
if (it != cacheEnd) {
if (topLevelDirectory)
*topLevelDirectory = it.key();
+ if (debug)
+ qDebug("<findVersionControlForDirectory: cache match for VCS '%s', topLevel: %s",
+ qPrintable(it.value()->displayName()), qPrintable(it.key()));
return it.value();
}
- pos = index + 1;
+ pos = index - 1;
}
// Nothing: ask the IVersionControls directly, insert the toplevel into the cache.
const VersionControlList versionControls = allVersionControls();
foreach (IVersionControl * versionControl, versionControls) {
- if (versionControl->managesDirectory(directory)) {
- const QString topLevel = versionControl->findTopLevelForDirectory(directory);
+ QString topLevel;
+ if (versionControl->managesDirectory(directory, &topLevel)) {
m_d->m_cachedMatches.insert(topLevel, versionControl);
if (topLevelDirectory)
*topLevelDirectory = topLevel;
+ if (debug)
+ qDebug("<findVersionControlForDirectory: invocation of '%s' matches: %s",
+ qPrintable(versionControl->displayName()), qPrintable(topLevel));
return versionControl;
}
}
+ if (debug)
+ qDebug("<findVersionControlForDirectory: No match for %s", qPrintable(directory));
return 0;
}
-<plugin name="CodePaster" version="1.3.84" compatVersion="1.3.84">
+<plugin name="CodePaster" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Codepaster plugin for pushing/fetching diff from server</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
-<plugin name="CppEditor" version="1.3.84" compatVersion="1.3.84">
+<plugin name="CppEditor" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>C/C++ editor component.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="CppTools" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="CppTools" version="2.0.80"/>
</dependencyList>
</plugin>
#include <TranslationUnit.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/TypeOfExpression.h>
-#include <cplusplus/LookupContext.h>
+#include <cplusplus/DeprecatedLookupContext.h>
#include <cplusplus/Overview.h>
#include <cplusplus/OverviewModel.h>
#include <cplusplus/SimpleLexer.h>
} // end of anonymous namespace
-static const QualifiedNameId *qualifiedNameIdForSymbol(Symbol *s, const LookupContext &context)
-{
- const Name *symbolName = s->name();
- if (! symbolName)
- return 0; // nothing to do.
-
- QVector<const Name *> names;
-
- for (Scope *scope = s->scope(); scope; scope = scope->enclosingScope()) {
- if (scope->isClassScope() || scope->isNamespaceScope()) {
- if (scope->owner() && scope->owner()->name()) {
- const Name *ownerName = scope->owner()->name();
- if (const QualifiedNameId *q = ownerName->asQualifiedNameId()) {
- for (unsigned i = 0; i < q->nameCount(); ++i) {
- names.prepend(q->nameAt(i));
- }
- } else {
- names.prepend(ownerName);
- }
- }
- }
- }
-
- if (const QualifiedNameId *q = symbolName->asQualifiedNameId()) {
- for (unsigned i = 0; i < q->nameCount(); ++i) {
- names.append(q->nameAt(i));
- }
- } else {
- names.append(symbolName);
- }
-
- return context.control()->qualifiedNameId(names.constData(), names.size());
-}
-
CPPEditorEditable::CPPEditorEditable(CPPEditor *editor)
: BaseTextEditorEditable(editor)
{
// qDebug() << "code:" << code;
TypeOfExpression typeOfExpression;
- typeOfExpression.setSnapshot(snapshot);
-
- Symbol *lastVisibleSymbol = doc->findSymbolAt(line, col);
+ typeOfExpression.init(doc, snapshot);
- const QList<LookupItem> results = typeOfExpression(code, doc,
- lastVisibleSymbol,
- TypeOfExpression::Preprocess);
+ const QList<LookupItem> results = typeOfExpression(code, doc->scopeAt(line, col),
+ TypeOfExpression::Preprocess);
NamespaceBindingPtr glo = bind(doc, snapshot);
- Symbol *canonicalSymbol = LookupContext::canonicalSymbol(results, glo.data());
+ Symbol *canonicalSymbol = DeprecatedLookupContext::canonicalSymbol(results, glo.data());
return canonicalSymbol;
}
semanticRehighlight();
}
-static bool isCompatible(const Name *name, const Name *otherName)
-{
- if (const NameId *nameId = name->asNameId()) {
- if (const TemplateNameId *otherTemplId = otherName->asTemplateNameId())
- return nameId->identifier()->isEqualTo(otherTemplId->identifier());
- } else if (const TemplateNameId *templId = name->asTemplateNameId()) {
- if (const NameId *otherNameId = otherName->asNameId())
- return templId->identifier()->isEqualTo(otherNameId->identifier());
- }
-
- return name->isEqualTo(otherName);
-}
-
-static bool isCompatible(Function *definition, Symbol *declaration,
- const QualifiedNameId *declarationName)
-{
- Function *declTy = declaration->type()->asFunctionType();
- if (! declTy)
- return false;
-
- const Name *definitionName = definition->name();
- if (const QualifiedNameId *q = definitionName->asQualifiedNameId()) {
- if (! isCompatible(q->unqualifiedNameId(), declaration->name()))
- return false;
- else if (q->nameCount() > declarationName->nameCount())
- return false;
- else if (declTy->argumentCount() != definition->argumentCount())
- return false;
- else if (declTy->isConst() != definition->isConst())
- return false;
- else if (declTy->isVolatile() != definition->isVolatile())
- return false;
-
- for (unsigned i = 0; i < definition->argumentCount(); ++i) {
- Symbol *arg = definition->argumentAt(i);
- Symbol *otherArg = declTy->argumentAt(i);
- if (! arg->type().isEqualTo(otherArg->type()))
- return false;
- }
-
- for (unsigned i = 0; i != q->nameCount(); ++i) {
- const Name *n = q->nameAt(q->nameCount() - i - 1);
- const Name *m = declarationName->nameAt(declarationName->nameCount() - i - 1);
- if (! isCompatible(n, m))
- return false;
- }
- return true;
- } else {
- // ### TODO: implement isCompatible for unqualified name ids.
- }
- return false;
-}
-
void CPPEditor::switchDeclarationDefinition()
{
- int line = 0, column = 0;
- convertPosition(position(), &line, &column);
-
- if (!m_modelManager)
+ if (! m_modelManager)
return;
const Snapshot snapshot = m_modelManager->snapshot();
- Document::Ptr doc = snapshot.document(file()->fileName());
- if (!doc)
- return;
- Symbol *lastSymbol = doc->findSymbolAt(line, column);
- if (!lastSymbol || !lastSymbol->scope())
- return;
-
- Function *f = lastSymbol->asFunction();
- if (!f) {
- Scope *fs = lastSymbol->scope();
- if (!fs->isFunctionScope())
- fs = fs->enclosingFunctionScope();
- if (fs)
- f = fs->owner()->asFunction();
- }
+ if (Document::Ptr thisDocument = snapshot.document(file()->fileName())) {
+ int line = 0, column = 0;
+ convertPosition(position(), &line, &column);
- if (f) {
- TypeOfExpression typeOfExpression;
- typeOfExpression.setSnapshot(m_modelManager->snapshot());
- QList<LookupItem> resolvedSymbols = typeOfExpression(QString(), doc, lastSymbol);
- const LookupContext &context = typeOfExpression.lookupContext();
+ Scope *scope = thisDocument->scopeAt(line, column);
+ Symbol *lastVisibleSymbol = thisDocument->lastVisibleSymbolAt(line, column);
- const QualifiedNameId *q = qualifiedNameIdForSymbol(f, context);
- QList<Symbol *> symbols = context.resolve(q);
+ Scope *functionScope = 0;
+ if (scope->isFunctionScope())
+ functionScope = scope;
+ else
+ functionScope = scope->enclosingFunctionScope();
- Symbol *declaration = 0;
- foreach (declaration, symbols) {
- if (isCompatible(f, declaration, q))
- break;
+ if (! functionScope && lastVisibleSymbol) {
+ if (Function *def = lastVisibleSymbol->asFunction())
+ functionScope = def->members();
}
- if (! declaration && ! symbols.isEmpty())
- declaration = symbols.first();
+ if (functionScope) {
+ LookupContext context(thisDocument, snapshot);
+
+ Function *functionDefinition = functionScope->owner()->asFunction();
+ const QList<Symbol *> declarations = context.lookup(functionDefinition->name(), functionDefinition->scope());
+ foreach (Symbol *decl, declarations) {
+ // TODO: check decl.
+ openCppEditorAt(linkToSymbol(decl));
+ break;
+ }
- if (declaration)
- openCppEditorAt(linkToSymbol(declaration));
- } else if (lastSymbol->type()->isFunctionType()) {
- if (Symbol *def = findDefinition(lastSymbol))
- openCppEditorAt(linkToSymbol(def));
+ } else if (lastVisibleSymbol && lastVisibleSymbol->isDeclaration() && lastVisibleSymbol->type()->isFunctionType()) {
+ if (Symbol *def = snapshot.findMatchingDefinition(lastVisibleSymbol))
+ openCppEditorAt(linkToSymbol(def));
+ }
}
}
}
// Find the last symbol up to the cursor position
- Symbol *lastSymbol = doc->findSymbolAt(line, column);
- if (!lastSymbol)
+ Scope *scope = doc->scopeAt(line, column);
+ if (!scope)
return link;
// Evaluate the type of the expression under the cursor
const QString expression = expressionUnderCursor(tc);
TypeOfExpression typeOfExpression;
- typeOfExpression.setSnapshot(snapshot);
- QList<LookupItem> resolvedSymbols =
- typeOfExpression(expression, doc, lastSymbol);
+ typeOfExpression.init(doc, snapshot);
+ const QList<LookupItem> resolvedSymbols = typeOfExpression(expression, scope, TypeOfExpression::Preprocess);
if (!resolvedSymbols.isEmpty()) {
- LookupItem result = skipForwardDeclarations(resolvedSymbols);
+ const LookupItem result = skipForwardDeclarations(resolvedSymbols);
- if (Symbol *symbol = result.lastVisibleSymbol()) {
+ if (Symbol *symbol = result.declaration()) {
Symbol *def = 0;
- if (resolveTarget && !lastSymbol->isFunction())
- def = findDefinition(symbol);
+
+ if (resolveTarget) {
+ def = findDefinition(symbol, snapshot);
+
+ if (def == doc->lastVisibleSymbolAt(line, column))
+ def = 0; // jump to declaration then.
+ }
link = linkToSymbol(def ? def : symbol);
link.begin = beginOfToken;
openLink(findLinkAt(textCursor()));
}
-Symbol *CPPEditor::findDefinition(Symbol *symbol)
+Symbol *CPPEditor::findDefinition(Symbol *symbol, const Snapshot &snapshot)
{
if (symbol->isFunction())
return 0; // symbol is a function definition.
- Function *funTy = symbol->type()->asFunctionType();
- if (! funTy)
- return 0; // symbol does not have function type.
-
- const Name *name = symbol->name();
- if (! name)
- return 0; // skip anonymous functions!
-
- if (const QualifiedNameId *q = name->asQualifiedNameId())
- name = q->unqualifiedNameId();
-
- // map from file names to function definitions.
- QMap<QString, QList<Function *> > functionDefinitions;
-
- // find function definitions.
- FindFunctionDefinitions findFunctionDefinitions;
-
- // save the current snapshot
- const Snapshot snapshot = m_modelManager->snapshot();
-
- foreach (Document::Ptr doc, snapshot) {
- if (Scope *globals = doc->globalSymbols()) {
- QList<Function *> *localFunctionDefinitions =
- &functionDefinitions[doc->fileName()];
-
- findFunctionDefinitions(name, globals,
- localFunctionDefinitions);
- }
- }
-
- // a dummy document.
- Document::Ptr expressionDocument = Document::create("<empty>");
-
- Function *bestMatch = 0;
- int bestScore = -1;
-
- QMapIterator<QString, QList<Function *> > it(functionDefinitions);
- while (it.hasNext()) {
- it.next();
-
- // get the instance of the document.
- Document::Ptr thisDocument = snapshot.document(it.key());
-
- foreach (Function *f, it.value()) {
- int score = 0; // current function's score
-
- const int funTyArgsCount = funTy->argumentCount();
- const int fArgsCount = f->argumentCount();
-
- // max score if arguments count equals
- if (funTyArgsCount == fArgsCount)
- score += funTyArgsCount + 1;
- else
- score += (funTyArgsCount < fArgsCount) ? funTyArgsCount : fArgsCount;
-
- // +1 to score for every equal parameter
- unsigned minCount = (funTyArgsCount < fArgsCount) ? funTyArgsCount : fArgsCount;
- for (unsigned i = 0; i < minCount; ++i)
- if (Symbol *funTyArg = funTy->argumentAt(i))
- if (Symbol *fArg = f->argumentAt(i))
- if (funTyArg->type().isEqualTo(fArg->type()))
- score++;
-
- if (score > bestScore) {
- // create a lookup context
- const LookupContext context(f, expressionDocument,
- thisDocument, snapshot);
-
- // search the matching definition for the function declaration `symbol'.
- foreach (Symbol *s, context.resolve(f->name())) {
- if (s == symbol) {
- bestMatch = f;
- bestScore = score;
- }
- }
- }
- }
- }
+ else if (! symbol->type()->isFunctionType())
+ return 0; // not a function declaration
- return bestMatch;
+ return snapshot.findMatchingDefinition(symbol);
}
unsigned CPPEditor::editorRevision() const
CPlusPlus::Symbol *markSymbols();
bool sortedMethodOverview() const;
- CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol);
+ CPlusPlus::Symbol *findDefinition(CPlusPlus::Symbol *symbol, const CPlusPlus::Snapshot &snapshot);
virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
TextEditor::ITextEditor *openCppEditorAt(const QString &fileName, int line,
const QList<SimpleToken> tokens = tokenize(text, initialState);
state = tokenize.state(); // refresh the state
+ int foldingIndent = initialBraceDepth;
+ if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) {
+ userData->setFoldingIndent(0);
+ userData->setFoldingStartIncluded(false);
+ userData->setFoldingEndIncluded(false);
+ }
+
if (tokens.isEmpty()) {
setCurrentBlockState(previousState);
- if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) {
- userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse);
- userData->setCollapseMode(TextBlockUserData::NoCollapse);
- }
BaseTextDocumentLayout::clearParentheses(currentBlock());
if (text.length()) // the empty line can still contain whitespace
setFormat(0, text.length(), m_formats[CppVisualWhitespace]);
+ BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
return;
}
if (tk.is(T_LPAREN) || tk.is(T_LBRACE) || tk.is(T_LBRACKET)) {
const QChar c(tk.text().at(0));
parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.position()));
- if (tk.is(T_LBRACE))
+ if (tk.is(T_LBRACE)) {
++braceDepth;
+
+ // if a folding block opens at the beginning of a line, treat the entire line
+ // as if it were inside the folding block
+ if (tk.position() == firstNonSpace) {
+ ++foldingIndent;
+ BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true);
+ }
+ }
} else if (tk.is(T_RPAREN) || tk.is(T_RBRACE) || tk.is(T_RBRACKET)) {
const QChar c(tk.text().at(0));
parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.position()));
- if (tk.is(T_RBRACE))
+ if (tk.is(T_RBRACE)) {
--braceDepth;
+ if (braceDepth < foldingIndent) {
+ // unless we are at the end of the block, we reduce the folding indent
+ if (i == tokens.size()-1 || tokens.at(i+1).is(T_SEMICOLON))
+ BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true);
+ else
+ foldingIndent = qMin(braceDepth, foldingIndent);
+ }
+ }
}
bool highlightCurrentWordAsPreprocessor = highlightAsPreprocessor;
// - is not a continuation line (tokens.size() > 1 || ! state)
if (initialState && i == 0 && (tokens.size() > 1 || ! state)) {
--braceDepth;
-
+ // unless we are at the end of the block, we reduce the folding indent
+ if (i == tokens.size()-1)
+ BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true);
+ else
+ foldingIndent = qMin(braceDepth, foldingIndent);
const int tokenEnd = tk.position() + tk.length() - 1;
parentheses.append(Parenthesis(Parenthesis::Closed, QLatin1Char('-'), tokenEnd));
else if (tk.is(T_IDENTIFIER))
highlightWord(tk.text(), tk.position(), tk.length());
+
}
// mark the trailing white spaces
highlightLine(text, lastTokenEnd, text.length() - lastTokenEnd, QTextCharFormat());
}
- if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) {
- userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse);
- userData->setCollapseMode(TextBlockUserData::NoCollapse);
- }
-
if (! initialState && state && ! tokens.isEmpty()) {
parentheses.append(Parenthesis(Parenthesis::Opened, QLatin1Char('+'),
tokens.last().position()));
++braceDepth;
}
- QChar c;
- int collapse = Parenthesis::collapseAtPos(parentheses, &c);
- if (collapse >= 0) {
- TextBlockUserData::CollapseMode collapseMode = TextBlockUserData::CollapseAfter;
- if (collapse == firstNonSpace && c != QLatin1Char('+'))
- collapseMode = TextBlockUserData::CollapseThis;
- BaseTextDocumentLayout::userData(currentBlock())->setCollapseMode(collapseMode);
- }
-
-
- int cc = Parenthesis::closeCollapseAtPos(parentheses);
- if (cc >= 0) {
- TextBlockUserData *userData = BaseTextDocumentLayout::userData(currentBlock());
- userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapse);
- QString trailingText = text.mid(cc+1).simplified();
- if (trailingText.isEmpty() || trailingText == QLatin1String(";")) {
- userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapseAtEnd);
- }
- }
-
BaseTextDocumentLayout::setParentheses(currentBlock(), parentheses);
// if the block is ifdefed out, we only store the parentheses, but
+
// do not adjust the brace depth.
- if (BaseTextDocumentLayout::ifdefedOut(currentBlock()))
+ if (BaseTextDocumentLayout::ifdefedOut(currentBlock())) {
braceDepth = initialBraceDepth;
+ foldingIndent = initialBraceDepth;
+ }
+
+ BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
// optimization: if only the brace depth changes, we adjust subsequent blocks
// to have QSyntaxHighlighter stop the rehighlighting
QTextBlock block = currentBlock().next();
while (block.isValid() && block.userState() != -1) {
BaseTextDocumentLayout::changeBraceDepth(block, delta);
+ BaseTextDocumentLayout::changeFoldingIndent(block, delta);
block = block.next();
}
}
return qualifiedNames.join(QLatin1String("::"));
}
-// ### move me
-static FullySpecifiedType resolve(const FullySpecifiedType &ty,
- const LookupContext &context,
- Symbol **resolvedSymbol,
- const Name **resolvedName)
-{
- Control *control = context.control();
-
- if (const PointerType *ptrTy = ty->asPointerType()) {
- return control->pointerType(resolve(ptrTy->elementType(), context,
- resolvedSymbol, resolvedName));
-
- } else if (const ReferenceType *refTy = ty->asReferenceType()) {
- return control->referenceType(resolve(refTy->elementType(), context,
- resolvedSymbol, resolvedName));
-
- } else if (const PointerToMemberType *ptrToMemTy = ty->asPointerToMemberType()) {
- return control->pointerToMemberType(ptrToMemTy->memberName(),
- resolve(ptrToMemTy->elementType(), context,
- resolvedSymbol, resolvedName));
-
- } else if (const NamedType *namedTy = ty->asNamedType()) {
- if (resolvedName)
- *resolvedName = namedTy->name();
-
- const QList<Symbol *> candidates = context.resolve(namedTy->name());
-
- foreach (Symbol *c, candidates) {
- if (c->isClass() || c->isEnum()) {
- if (resolvedSymbol)
- *resolvedSymbol = c;
-
- return c->type();
- }
- }
-
- } else if (const Namespace *nsTy = ty->asNamespaceType()) {
- if (resolvedName)
- *resolvedName = nsTy->name();
-
- if (resolvedSymbol)
- *resolvedSymbol = const_cast<Namespace *>(nsTy);
-
- } else if (const Class *classTy = ty->asClassType()) {
- if (resolvedName)
- *resolvedName = classTy->name();
-
- if (resolvedSymbol)
- *resolvedSymbol = const_cast<Class *>(classTy);
-
- } else if (const ForwardClassDeclaration *fwdClassTy = ty->asForwardClassDeclarationType()) {
- if (resolvedName)
- *resolvedName = fwdClassTy->name();
-
- if (resolvedSymbol)
- *resolvedSymbol = const_cast<ForwardClassDeclaration *>(fwdClassTy);
-
- } else if (const Enum *enumTy = ty->asEnumType()) {
- if (resolvedName)
- *resolvedName = enumTy->name();
-
- if (resolvedSymbol)
- *resolvedSymbol = const_cast<Enum *>(enumTy);
-
- } else if (const Function *funTy = ty->asFunctionType()) {
- if (resolvedName)
- *resolvedName = funTy->name();
-
- if (resolvedSymbol)
- *resolvedSymbol = const_cast<Function *>(funTy);
-
- }
-
- return ty;
-}
-
void CppHoverHandler::updateHelpIdAndTooltip(TextEditor::ITextEditor *editor, int pos)
{
m_helpId.clear();
if (!edit)
return;
- const Snapshot documents = m_modelManager->snapshot();
+ const Snapshot snapshot = m_modelManager->snapshot();
const QString fileName = editor->file()->fileName();
- Document::Ptr doc = documents.document(fileName);
+ Document::Ptr doc = snapshot.document(fileName);
if (!doc)
return; // nothing to do
// Find the last symbol up to the cursor position
int line = 0, column = 0;
editor->convertPosition(tc.position(), &line, &column);
- Symbol *lastSymbol = doc->findSymbolAt(line, column);
+ Scope *scope = doc->scopeAt(line, column);
TypeOfExpression typeOfExpression;
- typeOfExpression.setSnapshot(documents);
+ typeOfExpression.init(doc, snapshot);
// We only want to show F1 if the tooltip matches the help id
bool showF1 = true;
ExpressionUnderCursor expressionUnderCursor;
const QString expression = expressionUnderCursor(tc);
- const QList<LookupItem> types = typeOfExpression(expression, doc, lastSymbol);
+ const QList<LookupItem> types = typeOfExpression(expression, scope);
- if (!types.isEmpty()) {
- const LookupItem result = types.first();
- FullySpecifiedType firstType = result.type(); // result of `type of expression'.
- Symbol *lookupSymbol = result.lastVisibleSymbol(); // lookup symbol
-
- Symbol *resolvedSymbol = lookupSymbol;
- const Name *resolvedName = lookupSymbol ? lookupSymbol->name() : 0;
- firstType = resolve(firstType, typeOfExpression.lookupContext(),
- &resolvedSymbol, &resolvedName);
-
- if (resolvedSymbol && resolvedSymbol->scope()
- && resolvedSymbol->scope()->isClassScope()) {
- Class *enclosingClass = resolvedSymbol->scope()->owner()->asClass();
+ if (!types.isEmpty()) {
+ Overview overview;
+ overview.setShowArgumentNames(true);
+ overview.setShowReturnTypes(true);
+ overview.setShowFullyQualifiedNamed(true);
+
+ const LookupItem result = types.first(); // ### TODO: select the best candidate.
+ FullySpecifiedType symbolTy = result.type(); // result of `type of expression'.
+ Symbol *declaration = result.declaration(); // lookup symbol
+ const Name *declarationName = declaration ? declaration->name() : 0;
+
+ if (declaration && declaration->scope()
+ && declaration->scope()->isClassScope()) {
+ Class *enclosingClass = declaration->scope()->owner()->asClass();
if (const Identifier *id = enclosingClass->identifier()) {
- if (id->isEqualTo(resolvedSymbol->identifier()))
- resolvedSymbol = enclosingClass;
+ if (id->isEqualTo(declaration->identifier()))
+ declaration = enclosingClass;
}
}
- m_helpId = buildHelpId(resolvedSymbol, resolvedName);
+ m_helpId = buildHelpId(declaration, declarationName);
if (m_toolTip.isEmpty()) {
- Symbol *symbol = result.lastVisibleSymbol();
- if (resolvedSymbol)
- symbol = resolvedSymbol;
+ Symbol *symbol = declaration;
- Overview overview;
- overview.setShowArgumentNames(true);
- overview.setShowReturnTypes(true);
- overview.setShowFullyQualifiedNamed(true);
+ if (declaration)
+ symbol = declaration;
- if (symbol && symbol == resolvedSymbol && symbol->isClass()) {
+ if (symbol && symbol == declaration && symbol->isClass()) {
m_toolTip = m_helpId;
- } else if (lookupSymbol && (lookupSymbol->isDeclaration() || lookupSymbol->isArgument())) {
- m_toolTip = overview.prettyType(firstType, buildHelpId(lookupSymbol, lookupSymbol->name()));
+ } else if (declaration && (declaration->isDeclaration() || declaration->isArgument())) {
+ m_toolTip = overview.prettyType(symbolTy, buildHelpId(declaration, declaration->name()));
- } else if (firstType->isClassType() || firstType->isEnumType() ||
- firstType->isForwardClassDeclarationType()) {
+ } else if (symbolTy->isClassType() || symbolTy->isEnumType() ||
+ symbolTy->isForwardClassDeclarationType()) {
m_toolTip = m_helpId;
} else {
- m_toolTip = overview.prettyType(firstType, m_helpId);
+ m_toolTip = overview.prettyType(symbolTy, m_helpId);
}
}
*/
const QList<LookupItem> QuickFixOperation::typeOf(CPlusPlus::ExpressionAST *ast)
{
+#ifdef __GNUC__
+# warning port me
+#endif
+
+ qWarning() << Q_FUNC_INFO << __LINE__;
+ return QList<LookupItem>();
+
+#if 0
unsigned line, column;
document()->translationUnit()->getTokenStartPosition(ast->firstToken(), &line, &column);
Symbol *lastVisibleSymbol = document()->findSymbolAt(line, column);
- _lookupContext = LookupContext(lastVisibleSymbol, document(), document(), snapshot());
-
- ResolveExpression resolveExpression(_lookupContext);
+ ResolveExpression resolveExpression(lastVisibleSymbol, _lookupContext);
return resolveExpression(ast);
+#endif
}
CPPQuickFixCollector::CPPQuickFixCollector()
#include <texteditor/icompletioncollector.h>
#include <cplusplus/CppDocument.h>
-#include <cplusplus/LookupContext.h>
+#include <cplusplus/DeprecatedLookupContext.h>
#include <ASTfwd.h>
#include <utils/changeset.h>
Utils::ChangeSet _changeSet;
CPPEditor *_editor;
CPlusPlus::AST *_topLevelNode;
- CPlusPlus::LookupContext _lookupContext;
+ CPlusPlus::DeprecatedLookupContext _lookupContext;
};
class CPPQuickFixCollector: public TextEditor::IQuickFixCollector
-<plugin name="CppTools" version="1.3.84" compatVersion="1.3.84">
+<plugin name="CppTools" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Tools for analyzing C/C++ code.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Locator" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Locator" version="2.0.80"/>
</dependencyList>
</plugin>
const CPlusPlus::Document::Ptr document = snapshot.document(fileName);
if (!document)
return QString();
- if (const CPlusPlus::Symbol *symbol = document->findSymbolAt(line, column))
+ if (const CPlusPlus::Symbol *symbol = document->lastVisibleSymbolAt(line, column))
if (const CPlusPlus::Scope *scope = symbol->scope())
if (const CPlusPlus::Scope *functionScope = scope->enclosingFunctionScope())
if (const CPlusPlus::Symbol *function = functionScope->owner()) {
#include <TranslationUnit.h>
#include <cplusplus/ResolveExpression.h>
-#include <cplusplus/LookupContext.h>
+#include <cplusplus/DeprecatedLookupContext.h>
#include <cplusplus/MatchingText.h>
#include <cplusplus/Overview.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <cplusplus/BackwardsScanner.h>
#include <cplusplus/TokenUnderCursor.h>
+#include <cplusplus/LookupContext.h>
#include <coreplugin/icore.h>
#include <coreplugin/mimedatabase.h>
#include <QtGui/QToolButton>
#include <QtGui/QVBoxLayout>
+namespace {
+ const bool debug = ! qgetenv("CPLUSPLUS_DEBUG").isEmpty();
+}
+
using namespace CPlusPlus;
namespace CppTools {
completionKind = T_SLASH;
--start;
break;
+ case '#':
+ completionKind = T_POUND;
+ --start;
+ break;
}
if (start == pos)
{
const int pos = editor->position();
if (startOfOperator(editor, pos, /*token =*/ 0, /*want function call=*/ true) != pos) {
+ if (m_completionOperator == T_POUND) {
+ if (TextEditor::BaseTextEditor *edit = qobject_cast<TextEditor::BaseTextEditor *>(editor->widget())) {
+ QTextCursor tc(edit->document());
+ tc.setPosition(pos);
+ return tc.columnNumber() == 1;
+ }
+
+ return false;
+ }
+
return true;
}
return m_startPosition;
}
+ // Pre-processor completion
+ if (m_completionOperator == T_POUND) {
+ completePreprocessor();
+ m_startPosition = startOfName;
+ return m_startPosition;
+ }
+
// Include completion
if (m_completionOperator == T_STRING_LITERAL
|| m_completionOperator == T_ANGLE_STRING_LITERAL
if (! thisDocument)
return -1;
- typeOfExpression.setSnapshot(snapshot);
- Symbol *lastVisibleSymbol = thisDocument->findSymbolAt(line, column);
+ typeOfExpression.init(thisDocument, snapshot);
+ Symbol *lastVisibleSymbol = thisDocument->lastVisibleSymbolAt(line, column);
if (expression.isEmpty()) {
if (m_completionOperator == T_EOF_SYMBOL || m_completionOperator == T_COLON_COLON)
}
}
+ Scope *scope = thisDocument->scopeAt(line, column);
+ Q_ASSERT(scope != 0);
- QList<LookupItem> results = typeOfExpression(expression, thisDocument, lastVisibleSymbol, TypeOfExpression::Preprocess);
- LookupContext context = typeOfExpression.lookupContext();
+ if (debug)
+ qDebug() << "scope:" << scope->owner()->fileName() << scope->owner()->line() << scope->owner()->column();
+
+ QList<LookupItem> results = typeOfExpression(expression, scope, TypeOfExpression::Preprocess);
+
+ if (debug)
+ qDebug() << "got:" << results.size() << "results";
if (results.isEmpty()) {
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
if (! (expression.isEmpty() || expression == QLatin1String("this"))) {
expression = QLatin1String("this");
- results = typeOfExpression(expression, thisDocument, lastVisibleSymbol);
+ results = typeOfExpression(expression, scope);
}
if (results.isEmpty())
return -1;
- context = typeOfExpression.lookupContext();
-
} else if (m_completionOperator == T_LPAREN) {
// Find the expression that precedes the current name
int index = endOfExpression;
// Resolve the type of this expression
const QList<LookupItem> results =
- typeOfExpression(baseExpression, thisDocument,
- lastVisibleSymbol,
+ typeOfExpression(baseExpression, scope,
TypeOfExpression::Preprocess);
// If it's a class, add completions for the constructors
foreach (const LookupItem &result, results) {
if (result.type()->isClassType()) {
- if (completeConstructorOrFunction(results, context, endOfExpression, true))
+ if (completeConstructorOrFunction(results, endOfExpression, true))
return m_startPosition;
break;
switch (m_completionOperator) {
case T_LPAREN:
- if (completeConstructorOrFunction(results, context, endOfExpression, false))
+ if (completeConstructorOrFunction(results, endOfExpression, false))
return m_startPosition;
break;
case T_DOT:
case T_ARROW:
- if (completeMember(results, context))
+ if (completeMember(results))
return m_startPosition;
break;
case T_COLON_COLON:
- if (completeScope(results, context))
+ if (completeScope(results))
return m_startPosition;
break;
case T_SIGNAL:
- if (completeSignal(results, context))
+ if (completeSignal(results))
return m_startPosition;
break;
case T_SLOT:
- if (completeSlot(results, context))
+ if (completeSlot(results))
return m_startPosition;
break;
}
Document::Ptr exprDoc = Document::create(QLatin1String("<expression>"));
- const LookupContext context(lastVisibleSymbol, exprDoc, thisDocument, snapshot);
+ const DeprecatedLookupContext context(lastVisibleSymbol, exprDoc, thisDocument, snapshot);
const QList<Scope *> scopes = context.expand(context.visibleScopes());
foreach (Scope *scope, scopes) {
}
bool CppCodeCompletion::completeConstructorOrFunction(const QList<LookupItem> &results,
- const LookupContext &context,
int endOfExpression, bool toolTipOnly)
{
+ const LookupContext &context = typeOfExpression.context();
QList<Function *> functions;
foreach (const LookupItem &result, results) {
}
if (functions.isEmpty()) {
- ResolveExpression resolveExpression(context);
- ResolveClass resolveClass;
const Name *functionCallOp = context.control()->operatorNameId(OperatorNameId::FunctionCallOp);
foreach (const LookupItem &result, results) {
FullySpecifiedType ty = result.type().simplified();
+ Scope *scope = result.scope();
if (NamedType *namedTy = ty->asNamedType()) {
- const QList<Symbol *> classObjectCandidates = resolveClass(namedTy->name(), result, context);
-
- foreach (Symbol *classObjectCandidate, classObjectCandidates) {
- if (Class *klass = classObjectCandidate->asClass()) {
- const QList<LookupItem> overloads =
- resolveExpression.resolveMember(functionCallOp, klass,
- namedTy->name());
+ if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
+ foreach (Symbol *overload, b->lookup(functionCallOp)) {
+ FullySpecifiedType overloadTy = overload->type().simplified();
- foreach (const LookupItem &overloadResult, overloads) {
- FullySpecifiedType overloadTy = overloadResult.type().simplified();
-
- if (Function *funTy = overloadTy->asFunctionType())
- functions.append(funTy);
- }
+ if (Function *funTy = overloadTy->asFunctionType())
+ functions.append(funTy);
}
}
}
// find a scope that encloses the current location, starting from the lastVisibileSymbol
// and moving outwards
Scope *sc = 0;
- if (context.symbol())
- sc = context.symbol()->scope();
+ if (typeOfExpression.scope())
+ sc = typeOfExpression.scope();
else if (context.thisDocument())
sc = context.thisDocument()->globalSymbols();
m_functionArgumentWidget = new FunctionArgumentWidget;
m_functionArgumentWidget->showFunctionHint(functions,
- typeOfExpression.lookupContext(),
+ typeOfExpression.context(),
m_startPosition);
}
return false;
}
-bool CppCodeCompletion::completeMember(const QList<LookupItem> &baseResults,
- const LookupContext &context)
+bool CppCodeCompletion::completeMember(const QList<LookupItem> &baseResults)
{
+ const LookupContext &context = typeOfExpression.context();
+
+ if (debug)
+ qDebug() << Q_FUNC_INFO << __LINE__;
+
if (baseResults.isEmpty())
return false;
ResolveExpression resolveExpression(context);
- ResolveClass resolveClass;
bool replacedDotOperator = false;
- const QList<LookupItem> classObjectResults =
- resolveExpression.resolveBaseExpression(baseResults,
- m_completionOperator,
- &replacedDotOperator);
-
- QList<Symbol *> classObjectCandidates;
- foreach (const LookupItem &r, classObjectResults) {
- FullySpecifiedType ty = r.type().simplified();
- if (Class *klass = ty->asClassType())
- classObjectCandidates.append(klass);
+ if (ClassOrNamespace *binding = resolveExpression.baseExpression(baseResults,
+ m_completionOperator,
+ &replacedDotOperator)) {
+ if (debug)
+ qDebug() << "cool we got a binding for the base expression";
+
+ if (replacedDotOperator && binding) {
+ // Replace . with ->
+ int length = m_editor->position() - m_startPosition + 1;
+ m_editor->setCurPos(m_startPosition - 1);
+ m_editor->replace(length, QLatin1String("->"));
+ ++m_startPosition;
+ }
- else if (NamedType *namedTy = ty->asNamedType()) {
- const Name *className = namedTy->name();
- const QList<Symbol *> classes = resolveClass(className, r, context);
+ if (binding)
+ completeClass(binding, /*static lookup = */ false);
- foreach (Symbol *c, classes) {
- if (Class *klass = c->asClass())
- classObjectCandidates.append(klass);
- }
- }
+ return ! m_completions.isEmpty();
}
- if (replacedDotOperator && ! classObjectCandidates.isEmpty()) {
- // Replace . with ->
- int length = m_editor->position() - m_startPosition + 1;
- m_editor->setCurPos(m_startPosition - 1);
- m_editor->replace(length, QLatin1String("->"));
- ++m_startPosition;
+ if (debug) {
+ Overview oo;
+ qDebug() << "hmm, got:" << oo(baseResults.first().type());
}
- completeClass(classObjectCandidates, context, /*static lookup = */ false);
- if (! m_completions.isEmpty())
- return true;
-
return false;
}
-bool CppCodeCompletion::completeScope(const QList<LookupItem> &results,
- const LookupContext &context)
+bool CppCodeCompletion::completeScope(const QList<LookupItem> &results)
{
- QList<Symbol *> classes, namespaces;
+ const LookupContext &context = typeOfExpression.context();
+ if (results.isEmpty())
+ return false;
foreach (const LookupItem &result, results) {
FullySpecifiedType ty = result.type();
+ Scope *scope = result.scope();
- if (Class *classTy = ty->asClassType())
- classes.append(classTy);
+ if (NamedType *namedTy = ty->asNamedType()) {
+ if (ClassOrNamespace *b = context.lookupType(namedTy->name(), scope)) {
+ completeClass(b);
+ break;
+ }
- else if (Namespace *namespaceTy = ty->asNamespaceType())
- namespaces.append(namespaceTy);
- }
+ } else if (Class *classTy = ty->asClassType()) {
+ if (ClassOrNamespace *b = context.lookupType(classTy)) {
+ completeClass(b);
+ break;
+ }
- if (! classes.isEmpty())
- completeClass(classes, context);
+ } else if (Namespace *nsTy = ty->asNamespaceType()) {
+ if (ClassOrNamespace *b = context.lookupType(nsTy)) {
+ completeNamespace(b);
+ break;
+ }
- else if (! namespaces.isEmpty() && m_completions.isEmpty())
- completeNamespace(namespaces, context);
+ }
+ }
return ! m_completions.isEmpty();
}
return !m_completions.isEmpty();
}
-void CppCodeCompletion::completeNamespace(const QList<Symbol *> &candidates,
- const LookupContext &context)
+QStringList CppCodeCompletion::preprocessorCompletions
+ = QStringList()
+ << QLatin1String("define")
+ << QLatin1String("error")
+ << QLatin1String("include")
+ << QLatin1String("line")
+ << QLatin1String("pragma")
+ << QLatin1String("undef")
+ << QLatin1String("if")
+ << QLatin1String("ifdef")
+ << QLatin1String("ifndef")
+ << QLatin1String("elif")
+ << QLatin1String("else")
+ << QLatin1String("endif")
+ ;
+
+void CppCodeCompletion::completePreprocessor()
{
- QList<Scope *> todo;
- QList<Scope *> visibleScopes = context.visibleScopes();
- foreach (Symbol *candidate, candidates) {
- if (Namespace *ns = candidate->asNamespace())
- context.expand(ns->members(), visibleScopes, &todo);
+ TextEditor::CompletionItem item(this);
+
+ foreach (const QString &preprocessorCompletion, preprocessorCompletions) {
+ item.text = preprocessorCompletion;
+ m_completions.append(item);
}
- foreach (Scope *scope, todo) {
- if (! (scope->isNamespaceScope() || scope->isEnumScope()))
+ if (objcKeywordsWanted()) {
+ item.text = QLatin1String("import");
+ m_completions.append(item);
+ }
+}
+
+void CppCodeCompletion::completeNamespace(ClassOrNamespace *b)
+{
+ QSet<ClassOrNamespace *> bindingsVisited;
+ QList<ClassOrNamespace *> bindingsToVisit;
+ bindingsToVisit.append(b);
+
+ while (! bindingsToVisit.isEmpty()) {
+ ClassOrNamespace *binding = bindingsToVisit.takeFirst();
+ if (! binding || bindingsVisited.contains(binding))
continue;
- addCompletionItem(scope->owner());
+ bindingsVisited.insert(binding);
+ bindingsToVisit += binding->usings();
- for (unsigned i = 0; i < scope->symbolCount(); ++i) {
- addCompletionItem(scope->symbolAt(i));
+ QList<Scope *> scopesToVisit;
+ QSet<Scope *> scopesVisited;
+
+ foreach (Symbol *bb, binding->symbols()) {
+ if (Namespace *ns = bb->asNamespace())
+ scopesToVisit.append(ns->members());
+ }
+
+ foreach (Enum *e, binding->enums()) {
+ scopesToVisit.append(e->members());
+ }
+
+ while (! scopesToVisit.isEmpty()) {
+ Scope *scope = scopesToVisit.takeFirst();
+ if (! scope || scopesVisited.contains(scope))
+ continue;
+
+ scopesVisited.insert(scope);
+
+ for (Scope::iterator it = scope->firstSymbol(); it != scope->lastSymbol(); ++it) {
+ Symbol *member = *it;
+ addCompletionItem(member);
+ }
}
}
}
-void CppCodeCompletion::completeClass(const QList<Symbol *> &candidates,
- const LookupContext &context,
- bool staticLookup)
+void CppCodeCompletion::completeClass(ClassOrNamespace *b, bool staticLookup)
{
- if (candidates.isEmpty())
- return;
+ QSet<ClassOrNamespace *> bindingsVisited;
+ QList<ClassOrNamespace *> bindingsToVisit;
+ bindingsToVisit.append(b);
- Class *klass = candidates.first()->asClass();
+ while (! bindingsToVisit.isEmpty()) {
+ ClassOrNamespace *binding = bindingsToVisit.takeFirst();
+ if (! binding || bindingsVisited.contains(binding))
+ continue;
- QList<Scope *> todo;
- context.expand(klass->members(), context.visibleScopes(), &todo);
+ bindingsVisited.insert(binding);
+ bindingsToVisit += binding->usings();
- foreach (Scope *scope, todo) {
- if (! (scope->isClassScope() || scope->isEnumScope()))
- continue;
+ QList<Scope *> scopesToVisit;
+ QSet<Scope *> scopesVisited;
- addCompletionItem(scope->owner());
+ foreach (Symbol *bb, binding->symbols()) {
+ if (Class *k = bb->asClass())
+ scopesToVisit.append(k->members());
+ }
- for (unsigned i = 0; i < scope->symbolCount(); ++i) {
- Symbol *symbol = scope->symbolAt(i);
+ foreach (Enum *e, binding->enums())
+ scopesToVisit.append(e->members());
- if (symbol->type().isFriend())
- continue;
- else if (! staticLookup && (symbol->isTypedef() ||
- symbol->isEnum() ||
- symbol->isClass()))
+ while (! scopesToVisit.isEmpty()) {
+ Scope *scope = scopesToVisit.takeFirst();
+ if (! scope || scopesVisited.contains(scope))
continue;
- addCompletionItem(symbol);
+ scopesVisited.insert(scope);
+
+ for (Scope::iterator it = scope->firstSymbol(); it != scope->lastSymbol(); ++it) {
+ Symbol *member = *it;
+ if (member->isFriend())
+ continue;
+ else if (! staticLookup && (member->isTypedef() ||
+ member->isEnum() ||
+ member->isClass()))
+ continue;
+
+ addCompletionItem(member);
+ }
}
}
}
bool CppCodeCompletion::completeQtMethod(const QList<LookupItem> &results,
- const LookupContext &context,
bool wantSignals)
{
if (results.isEmpty())
return false;
- ResolveClass resolveClass;
+ const LookupContext &context = typeOfExpression.context();
+
+ DeprecatedLookupContext depContext(typeOfExpression.scope()->owner(),
+ context.expressionDocument(),
+ context.thisDocument(),
+ context.snapshot());
ConvertToCompletionItem toCompletionItem(this);
Overview o;
if (! namedTy) // not a class name.
continue;
- const QList<Symbol *> classObjects =
- resolveClass(namedTy->name(), p, context);
+ ClassOrNamespace *b = context.lookupType(namedTy->name(), p.scope());
+ if (! b)
+ continue;
+
+ const QList<Symbol *> classObjects = b->symbols();
if (classObjects.isEmpty())
continue;
- Class *klass = classObjects.first()->asClass();
+ Class *klass = 0;
+ foreach (Symbol *c, classObjects) {
+ klass = c->asClass();
+ if (klass != 0)
+ break;
+ }
+
+ if (! klass)
+ continue;
QList<Scope *> todo;
- const QList<Scope *> visibleScopes = context.visibleScopes(p);
- context.expand(klass->members(), visibleScopes, &todo);
+ const QList<Scope *> visibleScopes = depContext.visibleScopes(p);
+ depContext.expand(klass->members(), visibleScopes, &todo);
foreach (Scope *scope, todo) {
if (! scope->isClassScope())
}
} else if (! function->isAmbiguous()) {
if (completionSettings().m_spaceAfterFunctionName)
- extraChars += QLatin1Char(' ');
+ extraChars += QLatin1Char(' ');
extraChars += QLatin1Char('(');
// If the function doesn't return anything, automatically place the semicolon,
// Set empty map in order to avoid referencing old versions of the documents
// until the next completion
- typeOfExpression.setSnapshot(Snapshot());
+ typeOfExpression.reset();
}
int CppCodeCompletion::findStartOfName(int pos) const
class BaseTextEditor;
}
+namespace CPlusPlus {
+class LookupItem;
+class ClassOrNamespace;
+}
+
namespace CppTools {
namespace Internal {
void addCompletionItem(CPlusPlus::Symbol *symbol);
bool completeInclude(const QTextCursor &cursor);
+ void completePreprocessor();
int globalCompletion(CPlusPlus::Symbol *lastVisibleSymbol,
CPlusPlus::Document::Ptr thisDocument,
const CPlusPlus::Snapshot &snapshot);
- bool completeConstructorOrFunction(const QList<CPlusPlus::LookupItem> &,
- const CPlusPlus::LookupContext &,
+ bool completeConstructorOrFunction(const QList<CPlusPlus::LookupItem> &results,
int endOfExpression, bool toolTipOnly);
- bool completeMember(const QList<CPlusPlus::LookupItem> &,
- const CPlusPlus::LookupContext &context);
+ bool completeMember(const QList<CPlusPlus::LookupItem> &results);
+ bool completeScope(const QList<CPlusPlus::LookupItem> &results);
- bool completeScope(const QList<CPlusPlus::LookupItem> &,
- const CPlusPlus::LookupContext &context);
+ void completeNamespace(CPlusPlus::ClassOrNamespace *binding);
- void completeNamespace(const QList<CPlusPlus::Symbol *> &candidates,
- const CPlusPlus::LookupContext &context);
-
- void completeClass(const QList<CPlusPlus::Symbol *> &candidates,
- const CPlusPlus::LookupContext &context,
+ void completeClass(CPlusPlus::ClassOrNamespace *b,
bool staticLookup = true);
bool completeConstructors(CPlusPlus::Class *klass);
- bool completeQtMethod(const QList<CPlusPlus::LookupItem> &,
- const CPlusPlus::LookupContext &context,
+ bool completeQtMethod(const QList<CPlusPlus::LookupItem> &results,
bool wantSignals);
- bool completeSignal(const QList<CPlusPlus::LookupItem> &results,
- const CPlusPlus::LookupContext &context)
- { return completeQtMethod(results, context, true); }
+ bool completeSignal(const QList<CPlusPlus::LookupItem> &results)
+ { return completeQtMethod(results, true); }
- bool completeSlot(const QList<CPlusPlus::LookupItem> &results,
- const CPlusPlus::LookupContext &context)
- { return completeQtMethod(results, context, false); }
+ bool completeSlot(const QList<CPlusPlus::LookupItem> &results)
+ { return completeQtMethod(results, false); }
int findStartOfName(int pos = -1) const;
private:
bool objcKeywordsWanted() const;
+ static QStringList preprocessorCompletions;
+
CppModelManager *m_manager;
TextEditor::ITextEditable *m_editor;
int m_startPosition; // Position of the cursor from which completion started
#include <Parser.h>
#include <Control.h>
-#include <cplusplus/LookupContext.h>
+#include <cplusplus/DeprecatedLookupContext.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
{
QPointer<CppModelManager> _modelManager;
Snapshot _snapshot;
- CppModelManager::WorkingCopy _workingCopy;
Document::Ptr _doc;
+ Document::CheckMode _mode;
public:
Process(QPointer<CppModelManager> modelManager,
- Snapshot snapshot,
+ Document::Ptr doc,
+ const Snapshot &snapshot,
const CppModelManager::WorkingCopy &workingCopy)
: _modelManager(modelManager),
_snapshot(snapshot),
- _workingCopy(workingCopy)
- { }
-
- LookupContext lookupContext(unsigned line, unsigned column) const
- { return lookupContext(_doc->findSymbolAt(line, column)); }
-
- LookupContext lookupContext(Symbol *symbol) const
+ _doc(doc),
+ _mode(Document::FastCheck)
{
- LookupContext context(symbol, Document::create(QLatin1String("<none>")), _doc, _snapshot);
- return context;
+
+ if (workingCopy.contains(_doc->fileName()))
+ _mode = Document::FullCheck;
}
- void operator()(Document::Ptr doc)
+ void operator()()
{
- _doc = doc;
-
- Document::CheckMode mode = Document::FastCheck;
-
- if (_workingCopy.contains(doc->fileName()))
- mode = Document::FullCheck;
-
- if (doc->isParsed() && mode == Document::FastCheck) {
- TranslationUnit *unit = doc->translationUnit();
- MemoryPool *pool = unit->memoryPool();
+ _doc->check(_mode);
- Parser parser(unit);
- Semantic semantic(unit);
+ if (_mode == Document::FullCheck) {
+ // run the binding pass
+ NamespaceBindingPtr ns = bind(_doc, _snapshot);
- Namespace *globalNamespace = doc->control()->newNamespace(0);
- doc->setGlobalNamespace(globalNamespace);
+ // check for undefined symbols.
+ CheckUndefinedSymbols checkUndefinedSymbols(_doc);
+ checkUndefinedSymbols.setGlobalNamespaceBinding(ns);
- Scope *globals = globalNamespace->members();
-
- while (parser.LA()) {
- unsigned start_declaration = parser.cursor();
- DeclarationAST *declaration = 0;
-
- if (parser.parseDeclaration(declaration)) {
- semantic.check(declaration, globals);
-
- } else {
- doc->translationUnit()->error(start_declaration, "expected a declaration");
- parser.rewind(start_declaration + 1);
- parser.skipUntilDeclaration();
- }
-
- parser.clearTemplateArgumentList();
- pool->reset();
- }
-
- } else {
- doc->parse();
- doc->check(mode);
-
- if (mode == Document::FullCheck) {
- // run the binding pass
- NamespaceBindingPtr ns = bind(doc, _snapshot);
-
- // check for undefined symbols.
- CheckUndefinedSymbols checkUndefinedSymbols(doc);
- checkUndefinedSymbols.setGlobalNamespaceBinding(ns);
-
- checkUndefinedSymbols(doc->translationUnit()->ast()); // ### FIXME
- }
+ checkUndefinedSymbols(_doc->translationUnit()->ast()); // ### FIXME
}
- doc->releaseTranslationUnit();
+ _doc->releaseTranslationUnit();
if (_modelManager)
- _modelManager->emitDocumentUpdated(doc); // ### TODO: compress
+ _modelManager->emitDocumentUpdated(_doc); // ### TODO: compress
}
};
} // end of anonymous namespace
QString CppPreprocessor::tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision)
{
+ if (type == IncludeGlobal) {
+ const QString fn = m_fileNameCache.value(fileName);
+
+ if (! fn.isEmpty()) {
+ fileName = fn;
+
+ if (revision)
+ *revision = 0;
+
+ return QString();
+ }
+ }
+
+ const QString originalFileName = fileName;
+ const QString contents = tryIncludeFile_helper(fileName, type, revision);
+ if (type == IncludeGlobal)
+ m_fileNameCache.insert(originalFileName, fileName);
+ return contents;
+}
+
+QString CppPreprocessor::tryIncludeFile_helper(QString &fileName, IncludeType type, unsigned *revision)
+{
QFileInfo fileInfo(fileName);
if (fileName == QLatin1String(pp_configuration_file) || fileInfo.isAbsolute()) {
QString contents;
m_todo.remove(fileName);
#ifndef ICHECK_BUILD
- Process process(m_modelManager, snapshot, m_workingCopy);
+ Process process(m_modelManager, doc, snapshot, m_workingCopy);
- process(doc);
+ process();
(void) switchDocument(previousDoc);
#else
const CPlusPlus::Snapshot &snapshot)
{
NamespaceBindingPtr glo = bind(doc, snapshot);
- return m_findReferences->references(LookupContext::canonicalSymbol(symbol, glo.data()), doc, snapshot);
+ return m_findReferences->references(DeprecatedLookupContext::canonicalSymbol(symbol, glo.data()), doc, snapshot);
}
void CppModelManager::findUsages(CPlusPlus::Document::Ptr symbolDocument, CPlusPlus::Symbol *symbol)
future.reportFinished();
}
-// Function that sorts headers and sources apart to be used for
-// MimeDB::findByFile() on a sequence of file names.
-class HeaderSourceSorter {
-public:
- explicit HeaderSourceSorter(QStringList *sources, QStringList *headers);
- void operator()(const Core::MimeType &, const QFileInfo &, const QString &);
-
-private:
- QStringList m_sourceMimeTypes;
- QStringList m_headerMimeTypes;
-
- QStringList *m_sources;
- QStringList *m_headers;
-};
-
-HeaderSourceSorter::HeaderSourceSorter(QStringList *sources, QStringList *headers) :
- m_sources(sources),
- m_headers(headers)
-{
- m_headerMimeTypes << QLatin1String("text/x-hdr") << QLatin1String("text/x-c++hdr");
- m_sourceMimeTypes << QLatin1String("text/x-csrc") << QLatin1String("text/x-c++src")
- << QLatin1String("text/x-objcsrc");
-}
-
-void HeaderSourceSorter::operator()(const Core::MimeType &mimeType, const QFileInfo &, const QString &name)
-{
- if (mimeType) {
- if (m_sourceMimeTypes.contains(mimeType.type())) {
- m_sources->append(name);
- } else if (m_headerMimeTypes.contains(mimeType.type())) {
- m_headers->append(name);
- }
- }
-}
-
void CppModelManager::parse(QFutureInterface<void> &future,
CppPreprocessor *preproc,
QStringList files)
if (files.isEmpty())
return;
- foreach (const QString &file, files)
- preproc->snapshot.remove(file);
+ const Core::MimeDatabase *mimeDb = Core::ICore::instance()->mimeDatabase();
+ Core::MimeType cSourceTy = mimeDb->findByType(QLatin1String("text/x-csrc"));
+ Core::MimeType cppSourceTy = mimeDb->findByType(QLatin1String("text/x-c++src"));
+ Core::MimeType mSourceTy = mimeDb->findByType(QLatin1String("text/x-objcsrc"));
QStringList sources;
QStringList headers;
- const Core::MimeDatabase *mimeDb = Core::ICore::instance()->mimeDatabase();
- mimeDb->findByFile(files.constBegin(), files.constEnd(),
- HeaderSourceSorter(&sources, &headers));
+
+ QStringList suffixes = cSourceTy.suffixes();
+ suffixes += cppSourceTy.suffixes();
+ suffixes += mSourceTy.suffixes();
+
+ foreach (const QString &file, files) {
+ QFileInfo info(file);
+
+ preproc->snapshot.remove(file);
+
+ if (suffixes.contains(info.suffix()))
+ sources.append(file);
+ else
+ headers.append(file);
+ }
const int sourceCount = sources.size();
files = sources;
if (future.isCanceled())
break;
- // Change the priority of the background parser thread to idle.
- QThread::currentThread()->setPriority(QThread::IdlePriority);
-
const QString fileName = files.at(i);
const bool isSourceFile = i < sourceCount;
if (isSourceFile)
preproc->resetEnvironment();
-
- // Restore the previous thread priority.
- QThread::currentThread()->setPriority(QThread::NormalPriority);
}
future.setProgressValue(files.size());
};
#endif
-using namespace CPlusPlus;
class CppPreprocessor: public CPlusPlus::Client
{
public:
{ return m_todo; }
public: // attributes
- Snapshot snapshot;
+ CPlusPlus::Snapshot snapshot;
protected:
CPlusPlus::Document::Ptr switchDocument(CPlusPlus::Document::Ptr doc);
bool includeFile(const QString &absoluteFilePath, QString *result, unsigned *revision);
QString tryIncludeFile(QString &fileName, IncludeType type, unsigned *revision);
+ QString tryIncludeFile_helper(QString &fileName, IncludeType type, unsigned *revision);
void mergeEnvironment(CPlusPlus::Document::Ptr doc);
- virtual void macroAdded(const Macro ¯o);
- virtual void passedMacroDefinitionCheck(unsigned offset, const Macro ¯o);
+ virtual void macroAdded(const CPlusPlus::Macro ¯o);
+ virtual void passedMacroDefinitionCheck(unsigned offset, const CPlusPlus::Macro ¯o);
virtual void failedMacroDefinitionCheck(unsigned offset, const QByteArray &name);
virtual void startExpandingMacro(unsigned offset,
- const Macro ¯o,
+ const CPlusPlus::Macro ¯o,
const QByteArray &originalText,
bool inCondition,
- const QVector<MacroArgumentReference> &actuals);
- virtual void stopExpandingMacro(unsigned offset, const Macro ¯o);
+ const QVector<CPlusPlus::MacroArgumentReference> &actuals);
+ virtual void stopExpandingMacro(unsigned offset, const CPlusPlus::Macro ¯o);
virtual void startSkippingBlocks(unsigned offset);
virtual void stopSkippingBlocks(unsigned offset);
virtual void sourceNeeded(QString &fileName, IncludeType type,
#ifndef ICHECK_BUILD
QPointer<CppModelManager> m_modelManager;
#endif
- Environment env;
- Preprocessor preprocess;
+ CPlusPlus::Environment env;
+ CPlusPlus::Preprocessor preprocess;
QStringList m_includePaths;
QStringList m_systemIncludePaths;
CppModelManagerInterface::WorkingCopy m_workingCopy;
QStringList m_projectFiles;
QStringList m_frameworkPaths;
QSet<QString> m_included;
- Document::Ptr m_currentDoc;
+ CPlusPlus::Document::Ptr m_currentDoc;
QSet<QString> m_todo;
QSet<QString> m_processed;
unsigned m_revision;
+ QHash<QString, QString> m_fileNameCache;
};
} // namespace Internal
-<plugin name="CVS" version="1.3.84" compatVersion="1.3.84">
+<plugin name="CVS" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>CVS integration.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="VCSBase" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="VCSBase" version="2.0.80"/>
</dependencyList>
</plugin>
case DeleteOperation:
case AnnotateOperation:
break;
+ case MoveOperation:
case OpenOperation:
case CreateRepositoryOperation:
case SnapshotOperations:
return m_plugin->vcsDelete(fi.absolutePath(), fi.fileName());
}
+bool CVSControl::vcsMove(const QString &from, const QString &to)
+{
+ Q_UNUSED(from);
+ Q_UNUSED(to);
+ return false;
+}
+
bool CVSControl::vcsCreateRepository(const QString &)
{
return false;
return true;
}
-bool CVSControl::managesDirectory(const QString &directory) const
-{
- return m_plugin->managesDirectory(directory);
-}
-
-QString CVSControl::findTopLevelForDirectory(const QString &directory) const
+bool CVSControl::managesDirectory(const QString &directory, QString *topLevel) const
{
- return m_plugin->findTopLevelForDirectory(directory);
+ return m_plugin->managesDirectory(directory, topLevel);
}
void CVSControl::emitRepositoryChanged(const QString &s)
explicit CVSControl(CVSPlugin *plugin);
virtual QString displayName() const;
- virtual bool managesDirectory(const QString &directory) const;
- virtual QString findTopLevelForDirectory(const QString &directory) const;
+ virtual bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
virtual bool supportsOperation(Operation operation) const;
virtual bool vcsOpen(const QString &fileName);
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
+ virtual bool vcsMove(const QString &from, const QString &to);
virtual bool vcsCreateRepository(const QString &directory);
virtual QString vcsCreateSnapshot(const QString &topLevel);
virtual QStringList vcsSnapshots(const QString &topLevel);
bool CVSPlugin::describe(const QString &file, const QString &changeNr, QString *errorMessage)
{
- const QString toplevel = findTopLevelForDirectory(QFileInfo(file).absolutePath());
- if (toplevel.isEmpty()) {
+
+ QString toplevel;
+ const bool manages = managesDirectory(QFileInfo(file).absolutePath(), &toplevel);
+ if (!manages || toplevel.isEmpty()) {
*errorMessage = msgCannotFindTopLevel(file);
return false;
}
/* CVS has a "CVS" directory in each directory it manages. The top level
* is the first directory under the directory that does not have it. */
-bool CVSPlugin::managesDirectory(const QString &directory) const
+bool CVSPlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */) const
{
+ if (topLevel)
+ topLevel->clear();
+ bool manages = false;
const QDir dir(directory);
- const bool rc = dir.exists() && managesDirectory(dir);
- if (CVS::Constants::debug)
- qDebug() << "CVSPlugin::managesDirectory" << directory << rc;
- return rc;
+ do {
+ if (!dir.exists() || !checkCVSDirectory(dir))
+ break;
+ manages = true;
+ if (!topLevel)
+ break;
+ /* Recursing up, the top level is a child of the first directory that does
+ * not have a "CVS" directory. The starting directory must be a managed
+ * one. Go up and try to find the first unmanaged parent dir. */
+ QDir lastDirectory = dir;
+ for (QDir parentDir = lastDirectory; parentDir.cdUp() ; lastDirectory = parentDir) {
+ if (!checkCVSDirectory(parentDir)) {
+ *topLevel = lastDirectory.absolutePath();
+ break;
+ }
+ }
+ } while (false);
+ if (CVS::Constants::debug) {
+ QDebug nsp = qDebug().nospace();
+ nsp << "CVSPlugin::managesDirectory" << directory << manages;
+ if (topLevel)
+ nsp << *topLevel;
+ }
+ return manages;
}
-bool CVSPlugin::managesDirectory(const QDir &directory) const
+bool CVSPlugin::checkCVSDirectory(const QDir &directory) const
{
const QString cvsDir = directory.absoluteFilePath(QLatin1String("CVS"));
return QFileInfo(cvsDir).isDir();
}
-QString CVSPlugin::findTopLevelForDirectory(const QString &directory) const
-{
- // Debug wrapper
- const QString rc = findTopLevelForDirectoryI(directory);
- if (CVS::Constants::debug)
- qDebug() << "CVSPlugin::findTopLevelForDirectory" << directory << rc;
- return rc;
-}
-
-QString CVSPlugin::findTopLevelForDirectoryI(const QString &directory) const
-{
- /* Recursing up, the top level is a child of the first directory that does
- * not have a "CVS" directory. The starting directory must be a managed
- * one. Go up and try to find the first unmanaged parent dir. */
- QDir lastDirectory = QDir(directory);
- if (!lastDirectory.exists() || !managesDirectory(lastDirectory))
- return QString();
- for (QDir parentDir = lastDirectory; parentDir.cdUp() ; lastDirectory = parentDir) {
- if (!managesDirectory(parentDir))
- return lastDirectory.absolutePath();
- }
- return QString();
-}
-
CVSControl *CVSPlugin::cvsVersionControl() const
{
return static_cast<CVSControl *>(versionControl());
// IVersionControl
bool vcsAdd(const QString &workingDir, const QString &fileName);
bool vcsDelete(const QString &workingDir, const QString &fileName);
- bool managesDirectory(const QString &directory) const;
- QString findTopLevelForDirectory(const QString &directory) const;
+ bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
static CVSPlugin *cvsPluginInstance();
void filelog(const QString &workingDir,
const QStringList &files = QStringList(),
bool enableAnnotationContextMenu = false);
- bool managesDirectory(const QDir &directory) const;
+ bool checkCVSDirectory(const QDir &directory) const;
QString findTopLevelForDirectoryI(const QString &directory) const;
void startCommit(const QString &workingDir, const QStringList &files = QStringList());
bool commit(const QString &messageFile, const QStringList &subVersionFileList);
-<plugin name="Debugger" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Debugger" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Debugger integration.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="CppEditor" version="1.3.84"/><!-- Debugger plugin adds items to the editor's context menu -->
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="Find" version="1.3.84"/>
+ <dependency name="CppEditor" version="2.0.80"/><!-- Debugger plugin adds items to the editor's context menu -->
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="Find" version="2.0.80"/>
</dependencyList>
<argumentList>
<argument name="-disable-cdb">Disable Cdb debugger engine</argument>
<argument name="-disable-gdb">Disable Gdb debugger engine</argument>
<argument name="-disable-sdb">Disable Qt Script debugger engine</argument>
+ <argument name="-disable-tcf">Disable Tcf debugger engine</argument>
<argument name="-debug" parameter="pid-or-corefile">Attach to Process-Id or Core file</argument>
<argument name="-wincrashevent" parameter="event-handle">Event handle used for attaching to crashed processes</argument>
</argumentList>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AttachTcfDialog</class>
+ <widget class="QDialog" name="AttachTcfDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>310</width>
+ <height>224</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Start Debugger</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::ExpandingFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="channelLabel">
+ <property name="text">
+ <string>Host and port:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="channelLineEdit">
+ <property name="text">
+ <string notr="true">localhost:5115</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="architectureLabel">
+ <property name="text">
+ <string>Architecture:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="architectureComboBox"/>
+ </item>
+ <item row="2" column="1">
+ <widget class="QCheckBox" name="useServerStartScriptCheckBox"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="useServerStartScriptLabel">
+ <property name="text">
+ <string>Use server start script:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="Utils::PathChooser" name="serverStartScript" native="true"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="serverStartScriptLabel">
+ <property name="text">
+ <string>Server start script:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::PathChooser</class>
+ <extends>QWidget</extends>
+ <header location="global">utils/pathchooser.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
+ <widget class="QLabel" name="labelFileName">
+ <property name="text">
+ <string>File name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="lineEditFileName"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelLineNumber">
+ <property name="text">
+ <string>Line number:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="lineEditLineNumber"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelFunction">
+ <property name="text">
+ <string>Function:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="lineEditFunction"/>
+ </item>
+ <item row="3" column="0">
<widget class="QLabel" name="labelCondition">
<property name="text">
<string>Condition:</string>
</property>
</widget>
</item>
- <item row="0" column="1">
+ <item row="3" column="1">
<widget class="QLineEdit" name="lineEditCondition"/>
</item>
- <item row="1" column="0">
+ <item row="4" column="0">
<widget class="QLabel" name="labelIgnoreCount">
<property name="text">
<string>Ignore count:</string>
</property>
</widget>
</item>
- <item row="1" column="1">
- <widget class="QSpinBox" name="spinBoxIgnoreCount">
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="maximum">
- <number>999999999</number>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="lineEditIgnoreCount"/>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="labelThreadSpec">
+ <property name="text">
+ <string>Thread specification:</string>
</property>
</widget>
</item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="lineEditThreadSpec"/>
+ </item>
</layout>
</item>
<item>
#include "debuggeractions.h"
#include "debuggermanager.h"
+#include "debuggerstringutils.h"
#include "stackframe.h"
#include <texteditor/basetextmark.h>
#include <utils/qtcassert.h>
+#include <QtCore/QByteArray>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
#include <QtCore/QFileInfo>
// Compare file names case insensitively on Windows.
static inline bool fileNameMatch(const QString &f1, const QString &f2)
{
- return f1.compare(f2,
#ifdef Q_OS_WIN
- Qt::CaseInsensitive
+ return f1.compare(f2, Qt::CaseInsensitive) == 0;
#else
- Qt::CaseSensitive
+ return f1 == f2;
#endif
- ) == 0;
}
namespace Debugger {
QIcon icon() const
{
- const BreakHandler *handler = DebuggerManager::instance()->breakHandler();
+ const BreakHandler *handler = m_data->handler();
if (!m_enabled)
return handler->disabledBreakpointIcon();
return m_pending ? handler->pendingBreakPointIcon() : handler->breakpointIcon();
return;
BreakHandler *handler = m_data->handler();
- handler->removeBreakpoint(handler->indexOf(m_data));
+ handler->removeBreakpoint(m_data);
handler->saveBreakpoints();
handler->updateMarkers();
}
//
//////////////////////////////////////////////////////////////////
-BreakpointData::BreakpointData(BreakHandler *handler)
+BreakpointData::BreakpointData()
{
- //qDebug() << "CREATE BREAKPOINTDATA" << this;
- m_handler = handler;
+ m_handler = 0;
enabled = true;
pending = true;
+ type = BreakpointType;
marker = 0;
m_markerLineNumber = 0;
bpMultiple = false;
-//#if defined(Q_OS_MAC)
-// // full names do not work on Mac/MI
useFullPath = false;
-//#else
-// //where = m_manager->shortName(data->fileName);
-// useFullPath = true;
-//#endif
}
BreakpointData::~BreakpointData()
{
removeMarker();
- //qDebug() << "DESTROY BREAKPOINTDATA" << this;
}
void BreakpointData::removeMarker()
<< "</td><td>" << m_markerLineNumber << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Breakpoint Number:")
<< "</td><td>" << bpNumber << "</td></tr>"
- << "<tr><td>" << BreakHandler::tr("Breakpoint Address:")
- << "</td><td>" << bpAddress << "</td></tr>"
+ << "<tr><td>" << BreakHandler::tr("Breakpoint Type:")
+ << "</td><td>"
+ << (type == BreakpointType ? BreakHandler::tr("Breakpoint")
+ : type == WatchpointType ? BreakHandler::tr("Watchpoint")
+ : BreakHandler::tr("Unknown breakpoint type"))
+ << "</td></tr>"
<< "</table><br><hr><table>"
<< "<tr><th>" << BreakHandler::tr("Property")
<< "</th><th>" << BreakHandler::tr("Requested")
<< "</td><td>" << funcName << "</td><td>" << bpFuncName << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Line Number:")
<< "</td><td>" << lineNumber << "</td><td>" << bpLineNumber << "</td></tr>"
+ << "<tr><td>" << BreakHandler::tr("Breakpoint Address:")
+ << "</td><td>" << address << "</td><td>" << bpAddress << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Corrected Line Number:")
<< "</td><td>-</td><td>" << bpCorrectedLineNumber << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Condition:")
<< "</td><td>" << condition << "</td><td>" << bpCondition << "</td></tr>"
<< "<tr><td>" << BreakHandler::tr("Ignore Count:")
<< "</td><td>" << ignoreCount << "</td><td>" << bpIgnoreCount << "</td></tr>"
+ << "<tr><td>" << BreakHandler::tr("Thread Specification:")
+ << "</td><td>" << threadSpec << "</td><td>" << bpThreadSpec << "</td></tr>"
<< "</table></body></html>";
return rc;
}
{
QString rc;
QTextStream str(&rc);
- str << BreakHandler::tr("Marker File:") << m_markerFileName << ' '
- << BreakHandler::tr("Marker Line:") << m_markerLineNumber << ' '
- << BreakHandler::tr("Breakpoint Number:") << bpNumber << ' '
- << BreakHandler::tr("Breakpoint Address:") << bpAddress << '\n'
- << BreakHandler::tr("File Name:")
+ str << BreakHandler::tr("Marker File:") << ' ' << m_markerFileName << '\n'
+ << BreakHandler::tr("Marker Line:") << ' ' << m_markerLineNumber << '\n'
+ << BreakHandler::tr("Breakpoint Number:") << ' ' << bpNumber << '\n'
+ << BreakHandler::tr("Breakpoint Type:") << ' '
+ << (type == BreakpointType ? BreakHandler::tr("Breakpoint")
+ : type == WatchpointType ? BreakHandler::tr("Watchpoint")
+ : BreakHandler::tr("Unknown breakpoint type")) << '\n'
+ << BreakHandler::tr("File Name:") << ' '
<< fileName << " -- " << bpFileName << '\n'
- << BreakHandler::tr("Function Name:")
+ << BreakHandler::tr("Function Name:") << ' '
<< funcName << " -- " << bpFuncName << '\n'
- << BreakHandler::tr("Line Number:")
+ << BreakHandler::tr("Line Number:") << ' '
<< lineNumber << " -- " << bpLineNumber << '\n'
- << BreakHandler::tr("Condition:")
+ << BreakHandler::tr("Breakpoint Address:") << ' '
+ << address << " -- " << bpAddress << '\n'
+ << BreakHandler::tr("Condition:") << ' '
<< condition << " -- " << bpCondition << '\n'
- << BreakHandler::tr("Ignore Count:")
- << ignoreCount << " -- " << bpIgnoreCount << '\n';
+ << BreakHandler::tr("Ignore Count:") << ' '
+ << ignoreCount << " -- " << bpIgnoreCount << '\n'
+ << BreakHandler::tr("Thread Specification:") << ' '
+ << threadSpec << " -- " << bpThreadSpec << '\n';
return rc;
}
BreakHandler::BreakHandler(DebuggerManager *manager, QObject *parent) :
QAbstractTableModel(parent),
- m_breakpointIcon(QLatin1String(":/debugger/images/breakpoint_16.png")),
- m_disabledBreakpointIcon(QLatin1String(":/debugger/images/breakpoint_disabled_16.png")),
- m_pendingBreakPointIcon(QLatin1String(":/debugger/images/breakpoint_pending_16.png")),
+ m_breakpointIcon(_(":/debugger/images/breakpoint_16.png")),
+ m_disabledBreakpointIcon(_(":/debugger/images/breakpoint_disabled_16.png")),
+ m_pendingBreakPointIcon(_(":/debugger/images/breakpoint_pending_16.png")),
+ m_watchpointIcon(_(":/debugger/images/watchpoint.png")),
m_manager(manager)
{
}
int BreakHandler::columnCount(const QModelIndex &parent) const
{
- return parent.isValid() ? 0 : 7;
+ return parent.isValid() ? 0 : 8;
}
int BreakHandler::rowCount(const QModelIndex &parent) const
m_inserted.clear();
}
-int BreakHandler::findBreakpoint(const BreakpointData &needle) const
+BreakpointData *BreakHandler::findSimilarBreakpoint(const BreakpointData &needle) const
{
// Search a breakpoint we might refer to.
for (int index = 0; index != size(); ++index) {
- const BreakpointData *data = at(index);
+ BreakpointData *data = m_bp[index];
// Clear hit.
- if (data->bpNumber == needle.bpNumber)
- return index;
+ if (data->bpNumber == needle.bpNumber
+ && !data->bpNumber.isEmpty()
+ && data->bpNumber.toInt() != 0)
+ return data;
+ // Clear miss.
+ if (data->type != needle.type)
+ continue;
+ // We have numbers, but they are different.
+ if (!data->bpNumber.isEmpty() && !needle.bpNumber.isEmpty()
+ && !data->bpNumber.startsWith(needle.bpNumber)
+ && !needle.bpNumber.startsWith(data->bpNumber))
+ continue;
// At least at a position we were looking for.
// FIXME: breaks multiple breakpoints at the same location
- if (fileNameMatch(data->fileName, needle.bpFileName)
+ if (!data->fileName.isEmpty()
+ && fileNameMatch(data->fileName, needle.bpFileName)
&& data->lineNumber == needle.bpLineNumber)
- return index;
+ return data;
}
- return -1;
-}
-
-int BreakHandler::findBreakpoint(const QString &fileName, int lineNumber) const
-{
- if (lineNumber <= 0) {
- QByteArray address = fileName.toLatin1();
- for (int index = 0; index != size(); ++index)
- if (at(index)->bpAddress == address)
- return index;
- } else {
- for (int index = 0; index != size(); ++index)
- if (at(index)->isLocatedAt(fileName, lineNumber))
- return index;
- }
- return -1;
+ return 0;
}
-BreakpointData *BreakHandler::findBreakpoint(int bpNumber) const
+BreakpointData *BreakHandler::findBreakpointByNumber(int bpNumber) const
{
if (!size())
return 0;
return 0;
}
+int BreakHandler::findWatchPointIndexByAddress(const QByteArray &a) const
+{
+ for (int index = size() - 1; index >= 0; --index) {
+ BreakpointData *bd = at(index);
+ if (bd->type == BreakpointData::WatchpointType && bd->address == a)
+ return index;
+ }
+ return -1;
+}
+
+bool BreakHandler::watchPointAt(quint64 address) const
+{
+ const QByteArray addressBA = QByteArray("0x") + QByteArray::number(address, 16);
+ return findWatchPointIndexByAddress(addressBA) != -1;
+}
+
void BreakHandler::saveBreakpoints()
{
QList<QVariant> list;
for (int index = 0; index != size(); ++index) {
const BreakpointData *data = at(index);
QMap<QString, QVariant> map;
+ // Do not persist Watchpoints.
+ //if (data->type == BreakpointData::WatchpointType)
+ // continue;
+ if (data->type != BreakpointData::BreakpointType)
+ map.insert(_("type"), data->type);
if (!data->fileName.isEmpty())
- map.insert(QLatin1String("filename"), data->fileName);
+ map.insert(_("filename"), data->fileName);
if (!data->lineNumber.isEmpty())
- map.insert(QLatin1String("linenumber"), data->lineNumber);
+ map.insert(_("linenumber"), data->lineNumber);
if (!data->funcName.isEmpty())
- map.insert(QLatin1String("funcname"), data->funcName);
+ map.insert(_("funcname"), data->funcName);
+ if (!data->address.isEmpty())
+ map.insert(_("address"), data->address);
if (!data->condition.isEmpty())
- map.insert(QLatin1String("condition"), data->condition);
+ map.insert(_("condition"), data->condition);
if (!data->ignoreCount.isEmpty())
- map.insert(QLatin1String("ignorecount"), data->ignoreCount);
+ map.insert(_("ignorecount"), data->ignoreCount);
+ if (!data->threadSpec.isEmpty())
+ map.insert(_("threadspec"), data->threadSpec);
if (!data->enabled)
- map.insert(QLatin1String("disabled"), QLatin1String("1"));
+ map.insert(_("disabled"), _("1"));
if (data->useFullPath)
- map.insert(QLatin1String("usefullpath"), QLatin1String("1"));
+ map.insert(_("usefullpath"), _("1"));
list.append(map);
}
m_manager->setSessionValue("Breakpoints", list);
clear();
foreach (const QVariant &var, list) {
const QMap<QString, QVariant> map = var.toMap();
- BreakpointData *data = new BreakpointData(this);
- QVariant v = map.value(QLatin1String("filename"));
+ BreakpointData *data = new BreakpointData;
+ QVariant v = map.value(_("filename"));
if (v.isValid())
data->fileName = v.toString();
- v = map.value(QLatin1String("linenumber"));
+ v = map.value(_("linenumber"));
if (v.isValid())
data->lineNumber = v.toString().toLatin1();
- v = map.value(QLatin1String("condition"));
+ v = map.value(_("condition"));
if (v.isValid())
data->condition = v.toString().toLatin1();
- v = map.value(QLatin1String("ignorecount"));
+ v = map.value(_("address"));
+ if (v.isValid())
+ data->address = v.toString().toLatin1();
+ v = map.value(_("ignorecount"));
if (v.isValid())
data->ignoreCount = v.toString().toLatin1();
- v = map.value(QLatin1String("funcname"));
+ v = map.value(_("threadspec"));
+ if (v.isValid())
+ data->threadSpec = v.toString().toLatin1();
+ v = map.value(_("funcname"));
if (v.isValid())
data->funcName = v.toString();
- v = map.value(QLatin1String("disabled"));
+ v = map.value(_("disabled"));
if (v.isValid())
data->enabled = !v.toInt();
- v = map.value(QLatin1String("usefullpath"));
+ v = map.value(_("usefullpath"));
if (v.isValid())
data->useFullPath = bool(v.toInt());
+ v = map.value(_("type"));
+ if (v.isValid())
+ data->type = BreakpointData::Type(v.toInt());
data->setMarkerFileName(data->fileName);
data->setMarkerLineNumber(data->lineNumber.toInt());
append(data);
data->bpCorrectedLineNumber.clear();
data->bpCondition.clear();
data->bpIgnoreCount.clear();
+ data->bpThreadSpec.clear();
data->bpAddress.clear();
// Keep marker data if it was primary.
if (data->markerFileName() != data->fileName)
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
static QString headers[] = {
tr("Number"), tr("Function"), tr("File"), tr("Line"),
- tr("Condition"), tr("Ignore"), tr("Address")
+ tr("Condition"), tr("Ignore"), tr("Threads"), tr("Address")
};
return headers[section];
}
if (role == Qt::UserRole)
return data->enabled;
if (role == Qt::DecorationRole) {
+ if (data->type == BreakpointData::WatchpointType)
+ return m_watchpointIcon;
if (!data->enabled)
return m_disabledBreakpointIcon;
return data->pending ? m_pendingBreakPointIcon : m_breakpointIcon;
const QString str = data->pending ? data->funcName : data->bpFuncName;
return str.isEmpty() ? empty : str;
}
+ if (role == Qt::UserRole + 1)
+ return data->funcName;
break;
case 2:
if (role == Qt::DisplayRole) {
}
if (role == Qt::UserRole)
return data->useFullPath;
+ if (role == Qt::UserRole + 1)
+ return data->fileName;
break;
case 3:
if (role == Qt::DisplayRole) {
const QString str = data->pending ? data->lineNumber : data->bpLineNumber;
return str.isEmpty() ? empty : str;
}
+ if (role == Qt::UserRole + 1)
+ return data->lineNumber;
break;
case 4:
if (role == Qt::DisplayRole)
return data->pending ? data->condition : data->bpCondition;
if (role == Qt::ToolTipRole)
return tr("Breakpoint will only be hit if this condition is met.");
+ if (role == Qt::UserRole + 1)
+ return data->condition;
break;
case 5:
if (role == Qt::DisplayRole)
return data->pending ? data->ignoreCount : data->bpIgnoreCount;
if (role == Qt::ToolTipRole)
return tr("Breakpoint will only be hit after being ignored so many times.");
+ if (role == Qt::UserRole + 1)
+ return data->ignoreCount;
case 6:
- if (role == Qt::DisplayRole)
+ if (role == Qt::DisplayRole) {
+ if (data->pending)
+ return !data->threadSpec.isEmpty() ? data->threadSpec : tr("(all)");
+ else
+ return !data->bpThreadSpec.isEmpty() ? data->bpThreadSpec : tr("(all)");
+ }
+ if (role == Qt::ToolTipRole)
+ return tr("Breakpoint will only be hit in the specified thread(s).");
+ if (role == Qt::UserRole + 1)
+ return data->threadSpec;
+ case 7:
+ if (role == Qt::DisplayRole) {
+ if (data->type == BreakpointData::WatchpointType)
+ return data->address;
return data->bpAddress;
+ }
break;
}
if (role == Qt::ToolTipRole)
bool BreakHandler::setData(const QModelIndex &mi, const QVariant &value, int role)
{
- if (role != Qt::EditRole)
- return false;
-
BreakpointData *data = at(mi.row());
+
+ if (role == Qt::UserRole + 1) {
+ if (data->enabled != value.toBool()) {
+ toggleBreakpointEnabled(data);
+ layoutChanged();
+ }
+ return true;
+ }
+
+ if (role == Qt::UserRole + 2) {
+ if (data->useFullPath != value.toBool()) {
+ data->useFullPath = value.toBool();
+ layoutChanged();
+ }
+ return true;
+ }
+
+ //if (role != Qt::EditRole)
+ // return false;
+
switch (mi.column()) {
- case 0: {
- if (data->enabled != value.toBool()) {
- toggleBreakpointEnabled(data);
- dataChanged(mi, mi);
+ case 1: {
+ QString val = value.toString();
+ if (data->funcName != val) {
+ data->funcName = val;
+ layoutChanged();
}
return true;
}
case 2: {
- if (data->useFullPath != value.toBool()) {
- data->useFullPath = value.toBool();
- dataChanged(mi, mi);
+ QString val = value.toString();
+ if (data->fileName != val) {
+ data->fileName = val;
+ layoutChanged();
+ }
+ return true;
+ }
+ case 3: {
+ QByteArray val = value.toString().toLatin1();
+ if (data->lineNumber != val) {
+ data->lineNumber = val;
+ layoutChanged();
}
return true;
}
case 4: {
- QString val = value.toString();
+ QByteArray val = value.toString().toLatin1();
if (val != data->condition) {
- data->condition = val.toLatin1();
- dataChanged(mi, mi);
+ data->condition = val;
+ layoutChanged();
}
return true;
}
case 5: {
- QString val = value.toString();
+ QByteArray val = value.toString().toLatin1();
if (val != data->ignoreCount) {
- data->ignoreCount = val.toLatin1();
- dataChanged(mi, mi);
+ data->ignoreCount = val;
+ layoutChanged();
+ }
+ return true;
+ }
+ case 6: {
+ QByteArray val = value.toString().toLatin1();
+ if (val != data->threadSpec) {
+ data->threadSpec = val;
+ layoutChanged();
}
return true;
}
void BreakHandler::append(BreakpointData *data)
{
+ data->m_handler = this;
m_bp.append(data);
m_inserted.append(data);
}
saveBreakpoints();
}
+void BreakHandler::removeBreakpoint(BreakpointData *data)
+{
+ removeBreakpointHelper(m_bp.indexOf(data));
+ emit layoutChanged();
+ saveBreakpoints();
+}
+
void BreakHandler::toggleBreakpointEnabled(BreakpointData *data)
{
QTC_ASSERT(data, return);
&& data->ignoreCount.isEmpty())
return;
}
- BreakpointData *data = new BreakpointData(this);
+ BreakpointData *data = new BreakpointData;
data->funcName = functionName;
append(data);
saveBreakpoints();
#ifndef DEBUGGER_BREAKHANDLER_H
#define DEBUGGER_BREAKHANDLER_H
+#include "breakpoint.h"
+
#include <QtCore/QObject>
#include <QtCore/QAbstractTableModel>
#include <QtGui/QIcon>
namespace Debugger {
-class DebuggerManager;
namespace Internal {
-class BreakpointMarker;
-class BreakHandler;
-
-//////////////////////////////////////////////////////////////////
-//
-// BreakpointData
-//
-//////////////////////////////////////////////////////////////////
-
-class BreakpointData
-{
-public:
- explicit BreakpointData(BreakHandler *handler);
- ~BreakpointData();
-
- void removeMarker();
- void updateMarker();
- QString toToolTip() const;
- QString toString() const;
- BreakHandler *handler() { return m_handler; }
-
- bool isLocatedAt(const QString &fileName, int lineNumber) const;
- bool conditionsMatch() const;
-
-private:
- // Intentionally unimplemented.
- // Making it copyable is tricky because of the markers.
- void operator=(const BreakpointData &);
- BreakpointData(const BreakpointData &);
-
- // Our owner
- BreakHandler *m_handler; // Not owned.
-
-public:
- bool enabled; // Should we talk to the debugger engine?
- bool pending; // Does the debugger engine know about us already?
-
- // This "user requested information" will get stored in the session.
- QString fileName; // Short name of source file.
- QByteArray condition; // Condition associated with breakpoint.
- QByteArray ignoreCount; // Ignore count associated with breakpoint.
- QByteArray lineNumber; // Line in source file.
- QString funcName; // Name of containing function.
- bool useFullPath; // Should we use the full path when setting the bp?
-
- // This is what gdb produced in response.
- QByteArray bpNumber; // Breakpoint number assigned by the debugger engine.
- QByteArray bpCondition; // Condition acknowledged by the debugger engine.
- QByteArray bpIgnoreCount;// Ignore count acknowledged by the debugger engine.
- QString bpFileName; // File name acknowledged by the debugger engine.
- QString bpFullName; // Full file name acknowledged by the debugger engine.
- QByteArray bpLineNumber; // Line number acknowledged by the debugger engine.
- QByteArray bpCorrectedLineNumber; // Acknowledged by the debugger engine.
- QString bpFuncName; // Function name acknowledged by the debugger engine.
- QByteArray bpAddress; // Address acknowledged by the debugger engine.
- bool bpMultiple; // Happens in constructors/gdb.
- bool bpEnabled; // Enable/disable command sent.
-
- void setMarkerFileName(const QString &fileName);
- QString markerFileName() const { return m_markerFileName; }
-
- void setMarkerLineNumber(int lineNumber);
- int markerLineNumber() const { return m_markerLineNumber; }
-
-private:
- // Taken from either user input or gdb responses.
- QString m_markerFileName; // Used to locate the marker.
- int m_markerLineNumber;
-
- // Our red blob in the editor.
- BreakpointMarker *marker;
-};
-
-
//////////////////////////////////////////////////////////////////
//
// BreakHandler
void removeAt(int index); // This also deletes the marker.
void clear(); // This also deletes all the marker.
int indexOf(BreakpointData *data) { return m_bp.indexOf(data); }
- // If lineNumber < 0, interpret fileName as address.
- int findBreakpoint(const QString &fileName, int lineNumber) const;
- int findBreakpoint(const BreakpointData &data) const; // Returns index.
- BreakpointData *findBreakpoint(int bpNumber) const;
+ // Find a breakpoint matching approximately the data in needle.bp*,
+ BreakpointData *findSimilarBreakpoint(const BreakpointData &needle) const;
+ BreakpointData *findBreakpointByNumber(int bpNumber) const;
+ int findWatchPointIndexByAddress(const QByteArray &a) const;
+ bool watchPointAt(quint64 address) const;
void updateMarkers();
QList<BreakpointData *> insertedBreakpoints() const;
void toggleBreakpointEnabled(BreakpointData *data);
void breakByFunction(const QString &functionName);
void removeBreakpoint(int index);
+ void removeBreakpoint(BreakpointData *data);
private:
friend class BreakpointMarker;
const QIcon m_breakpointIcon;
const QIcon m_disabledBreakpointIcon;
const QIcon m_pendingBreakPointIcon;
+ const QIcon m_watchpointIcon;
DebuggerManager *m_manager; // Not owned.
QList<BreakpointData *> m_bp;
--- /dev/null
+/**************************************************************************
+**
+** 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 DEBUGGER_BREAKPOINT_H
+#define DEBUGGER_BREAKPOINT_H
+
+#include <QtCore/QString>
+
+namespace Debugger {
+class DebuggerManager;
+namespace Internal {
+
+class BreakpointMarker;
+class BreakHandler;
+
+//////////////////////////////////////////////////////////////////
+//
+// BreakpointData
+//
+//////////////////////////////////////////////////////////////////
+
+class BreakpointData
+{
+public:
+ BreakpointData();
+ ~BreakpointData();
+
+ void removeMarker();
+ void updateMarker();
+ QString toToolTip() const;
+ QString toString() const;
+ BreakHandler *handler() { return m_handler; }
+
+ bool isLocatedAt(const QString &fileName, int lineNumber) const;
+ bool conditionsMatch() const;
+
+private:
+ // Intentionally unimplemented.
+ // Making it copyable is tricky because of the markers.
+ void operator=(const BreakpointData &);
+ BreakpointData(const BreakpointData &);
+
+ // Our owner
+ BreakHandler *m_handler; // Not owned.
+ friend class BreakHandler;
+
+public:
+ enum Type { BreakpointType, WatchpointType };
+
+ bool enabled; // Should we talk to the debugger engine?
+ bool pending; // Does the debugger engine know about us already?
+ Type type; // Type of breakpoint.
+
+ // This "user requested information" will get stored in the session.
+ QString fileName; // Short name of source file.
+ QByteArray condition; // Condition associated with breakpoint.
+ QByteArray ignoreCount; // Ignore count associated with breakpoint.
+ QByteArray lineNumber; // Line in source file.
+ QByteArray address; // Address for watchpoints.
+ QByteArray threadSpec; // Thread specification.
+ QString funcName; // Name of containing function.
+ bool useFullPath; // Should we use the full path when setting the bp?
+
+ // This is what gdb produced in response.
+ QByteArray bpNumber; // Breakpoint number assigned by the debugger engine.
+ QByteArray bpCondition; // Condition acknowledged by the debugger engine.
+ QByteArray bpIgnoreCount;// Ignore count acknowledged by the debugger engine.
+ QString bpFileName; // File name acknowledged by the debugger engine.
+ QString bpFullName; // Full file name acknowledged by the debugger engine.
+ QByteArray bpLineNumber; // Line number acknowledged by the debugger engine.
+ QByteArray bpCorrectedLineNumber; // Acknowledged by the debugger engine.
+ QByteArray bpThreadSpec; // Thread spec acknowledged by the debugger engine.
+ QString bpFuncName; // Function name acknowledged by the debugger engine.
+ QByteArray bpAddress; // Address acknowledged by the debugger engine.
+ bool bpMultiple; // Happens in constructors/gdb.
+ bool bpEnabled; // Enable/disable command sent.
+
+ void setMarkerFileName(const QString &fileName);
+ QString markerFileName() const { return m_markerFileName; }
+
+ void setMarkerLineNumber(int lineNumber);
+ int markerLineNumber() const { return m_markerLineNumber; }
+
+private:
+ // Taken from either user input or gdb responses.
+ QString m_markerFileName; // Used to locate the marker.
+ int m_markerLineNumber;
+
+ // Our red blob in the editor.
+ BreakpointMarker *marker;
+};
+
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_BREAKPOINT_H
#include "breakwindow.h"
+#include "breakhandler.h"
+#include "threadshandler.h"
#include "debuggeractions.h"
#include "debuggermanager.h"
+#include "stackhandler.h"
#include "ui_breakcondition.h"
#include "ui_breakbyfunction.h"
void BreakWindow::showAddressColumn(bool on)
{
- setColumnHidden(6, !on);
+ setColumnHidden(7, !on);
}
static QModelIndexList normalizeIndexes(const QModelIndexList &list)
QTreeView::resizeEvent(ev);
}
+void BreakWindow::mouseDoubleClickEvent(QMouseEvent *ev)
+{
+ QModelIndex indexUnderMouse = indexAt(ev->pos());
+ if (indexUnderMouse.isValid() && indexUnderMouse.column() >= 4)
+ editBreakpoint(QModelIndexList() << indexUnderMouse);
+ QTreeView::mouseDoubleClickEvent(ev);
+}
+
void BreakWindow::contextMenuEvent(QContextMenuEvent *ev)
{
QMenu menu;
alwaysAdjustAction->setCheckable(true);
alwaysAdjustAction->setChecked(m_alwaysResizeColumnsToContents);
- QAction *editConditionAction =
- new QAction(tr("Edit Condition..."), &menu);
- editConditionAction->setEnabled(si.size() > 0);
+ QAction *editBreakpointAction =
+ new QAction(tr("Edit Breakpoint..."), &menu);
+ editBreakpointAction->setEnabled(si.size() > 0);
+
+ int threadId = m_manager->threadsHandler()->currentThreadId();
+ QString associateTitle = threadId == -1
+ ? tr("Associate Breakpoint With All Threads")
+ : tr("Associate Breakpoint With Thread %1").arg(threadId);
+ QAction *associateBreakpointAction = new QAction(associateTitle, &menu);
+ associateBreakpointAction->setEnabled(si.size() > 0);
QAction *synchronizeAction =
new QAction(tr("Synchronize Breakpoints"), &menu);
new QAction(tr("Set Breakpoint at \"catch\""), this);
menu.addAction(deleteAction);
- menu.addAction(editConditionAction);
+ menu.addAction(editBreakpointAction);
+ menu.addAction(associateBreakpointAction);
menu.addAction(toggleEnabledAction);
menu.addAction(pathAction);
menu.addSeparator();
resizeColumnsToContents();
else if (act == alwaysAdjustAction)
setAlwaysResizeColumnsToContents(!m_alwaysResizeColumnsToContents);
- else if (act == editConditionAction)
- editConditions(si);
+ else if (act == editBreakpointAction)
+ editBreakpoint(si);
+ else if (act == associateBreakpointAction)
+ associateBreakpoint(si, threadId);
else if (act == synchronizeAction)
emit breakpointSynchronizationRequested();
else if (act == toggleEnabledAction)
void BreakWindow::setBreakpointsEnabled(const QModelIndexList &list, bool enabled)
{
foreach (const QModelIndex &idx, list)
- model()->setData(idx, enabled);
+ model()->setData(idx, enabled, Qt::UserRole + 1);
emit breakpointSynchronizationRequested();
}
{
foreach (const QModelIndex &idx, list) {
QModelIndex idx2 = idx.sibling(idx.row(), 2);
- model()->setData(idx2, fullpath);
+ model()->setData(idx2, fullpath, Qt::UserRole + 2);
}
emit breakpointSynchronizationRequested();
}
const int row = qMin(firstRow, model()->rowCount() - 1);
if (row >= 0)
setCurrentIndex(model()->index(row, 0));
+ emit breakpointSynchronizationRequested();
}
-void BreakWindow::editConditions(const QModelIndexList &list)
+void BreakWindow::editBreakpoint(const QModelIndexList &list)
{
QDialog dlg(this);
Ui::BreakCondition ui;
QModelIndex idx = list.front();
int row = idx.row();
dlg.setWindowTitle(tr("Conditions on Breakpoint %1").arg(row));
- ui.lineEditCondition->setText(model()->data(idx.sibling(row, 4)).toString());
- ui.spinBoxIgnoreCount->setValue(model()->data(idx.sibling(row, 5)).toInt());
+ int role = Qt::UserRole + 1;
+ ui.lineEditFunction->hide();
+ ui.labelFunction->hide();
+ ui.lineEditFileName->hide();
+ ui.labelFileName->hide();
+ ui.lineEditLineNumber->hide();
+ ui.labelLineNumber->hide();
+ QAbstractItemModel *m = model();
+ //ui.lineEditFunction->setText(
+ // m->data(idx.sibling(row, 1), role).toString());
+ //ui.lineEditFileName->setText(
+ // m->data(idx.sibling(row, 2), role).toString());
+ //ui.lineEditLineNumber->setText(
+ // m->data(idx.sibling(row, 3), role).toString());
+ ui.lineEditCondition->setText(
+ m->data(idx.sibling(row, 4), role).toString());
+ ui.lineEditIgnoreCount->setText(
+ m->data(idx.sibling(row, 5), role).toString());
+ ui.lineEditThreadSpec->setText(
+ m->data(idx.sibling(row, 6), role).toString());
if (dlg.exec() == QDialog::Rejected)
return;
foreach (const QModelIndex &idx, list) {
- model()->setData(idx.sibling(idx.row(), 4), ui.lineEditCondition->text());
- model()->setData(idx.sibling(idx.row(), 5), ui.spinBoxIgnoreCount->value());
+ //m->setData(idx.sibling(idx.row(), 1), ui.lineEditFunction->text());
+ //m->setData(idx.sibling(idx.row(), 2), ui.lineEditFileName->text());
+ //m->setData(idx.sibling(idx.row(), 3), ui.lineEditLineNumber->text());
+ m->setData(idx.sibling(idx.row(), 4), ui.lineEditCondition->text());
+ m->setData(idx.sibling(idx.row(), 5), ui.lineEditIgnoreCount->text());
+ m->setData(idx.sibling(idx.row(), 6), ui.lineEditThreadSpec->text());
}
emit breakpointSynchronizationRequested();
}
+void BreakWindow::associateBreakpoint(const QModelIndexList &list, int threadId)
+{
+ QString str;
+ if (threadId != -1)
+ str = QString::number(threadId);
+ foreach (const QModelIndex &idx, list)
+ model()->setData(idx.sibling(idx.row(), 6), str);
+ emit breakpointSynchronizationRequested();
+}
+
void BreakWindow::resizeColumnsToContents()
{
for (int i = model()->columnCount(); --i >= 0; )
/**************************************************************************
+QT_END_NAMESPACE
**
** This file is part of Qt Creator
**
void resizeEvent(QResizeEvent *ev);
void contextMenuEvent(QContextMenuEvent *ev);
void keyPressEvent(QKeyEvent *ev);
+ void mouseDoubleClickEvent(QMouseEvent *ev);
private:
void deleteBreakpoints(const QModelIndexList &list);
void deleteBreakpoints(QList<int> rows);
- void editConditions(const QModelIndexList &list);
+ void editBreakpoint(const QModelIndexList &list);
+ void associateBreakpoint(const QModelIndexList &list, int thread);
void setBreakpointsEnabled(const QModelIndexList &list, bool enabled);
void setBreakpointsFullPath(const QModelIndexList &list, bool fullpath);
static const char sourceFileQuoteC = '`';
BreakPoint::BreakPoint() :
+ type(Code),
lineNumber(-1),
address(0),
+ threadId(-1),
ignoreCount(0),
oneShot(false),
enabled(true)
{
}
-int BreakPoint::compare(const BreakPoint& rhs) const
-{
- if (lineNumber > rhs.lineNumber)
- return 1;
- if (lineNumber < rhs.lineNumber)
- return -1;
- if (address > rhs.address)
- return 1;
- if (address < rhs.address)
- return -1;
- if (ignoreCount > rhs.ignoreCount)
- return 1;
- if (ignoreCount < rhs.ignoreCount)
- return -1;
- if (oneShot && !rhs.oneShot)
- return 1;
- if (!oneShot && rhs.oneShot)
- return -1;
- if (enabled && !rhs.enabled)
- return 1;
- if (!enabled && rhs.enabled)
- return -1;
- if (const int fileCmp = fileName.compare(rhs.fileName))
- return fileCmp;
- if (const int funcCmp = funcName.compare(rhs.funcName))
- return funcCmp;
- if (const int condCmp = condition.compare(rhs.condition))
- return condCmp;
- return 0;
-}
-
void BreakPoint::clear()
{
+ type = Code;
ignoreCount = 0;
+ threadId = -1;
oneShot = false;
enabled = true;
clearExpressionData();
QDebug operator<<(QDebug dbg, const BreakPoint &bp)
{
- QDebug nsp = dbg.nospace();
- if (bp.address)
- nsp << "0x" << QString::number(bp.address, 16) << ' ';
- if (!bp.fileName.isEmpty()) {
- nsp << "fileName='" << bp.fileName << ':' << bp.lineNumber << '\'';
+ dbg.nospace() << bp.toString();
+ return dbg;
+}
+
+QString BreakPoint::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << (type == BreakPoint::Code ? "Code " : "Data ");
+ if (address) {
+ str.setIntegerBase(16);
+ str << "0x" << address << ' ';
+ str.setIntegerBase(10);
+ }
+ if (!fileName.isEmpty()) {
+ str << "fileName='" << fileName << ':' << lineNumber << '\'';
} else {
- nsp << "funcName='" << bp.funcName << '\'';
+ str << "funcName='" << funcName << '\'';
}
- if (!bp.condition.isEmpty())
- nsp << " condition='" << bp.condition << '\'';
- if (bp.ignoreCount)
- nsp << " ignoreCount=" << bp.ignoreCount;
- if (bp.enabled)
- nsp << " enabled";
- if (bp.oneShot)
- nsp << " oneShot";
- return dbg;
+ if (threadId >= 0)
+ str << " thread=" << threadId;
+ if (!condition.isEmpty())
+ str << " condition='" << condition << '\'';
+ if (ignoreCount)
+ str << " ignoreCount=" << ignoreCount;
+ str << (enabled ? " enabled" : " disabled");
+ if (oneShot)
+ str << " oneShot";
+ return rc;
}
QString BreakPoint::expression() const
return rc;
}
+static inline QString msgCannotSetBreakpoint(const QString &exp, const QString &why)
+{
+ return QString::fromLatin1("Unable to set breakpoint '%1' : %2").arg(exp, why);
+}
+
bool BreakPoint::apply(CIDebugBreakpoint *ibp, QString *errorMessage) const
{
const QString expr = expression();
if (debugBP)
qDebug() << Q_FUNC_INFO << *this << expr;
- const HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
+ HRESULT hr = ibp->SetOffsetExpressionWide(reinterpret_cast<PCWSTR>(expr.utf16()));
if (FAILED(hr)) {
- *errorMessage = QString::fromLatin1("Unable to set breakpoint '%1' : %2").
- arg(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
+ *errorMessage = msgCannotSetBreakpoint(expr, CdbCore::msgComFailed("SetOffsetExpressionWide", hr));
return false;
}
// Pass Count is ignoreCount + 1
- ibp->SetPassCount(ignoreCount + 1u);
+ hr = ibp->SetPassCount(ignoreCount + 1u);
+ if (FAILED(hr))
+ qWarning("Error setting passcount %d %s ", ignoreCount, qPrintable(expr));
+ // Set up size for data breakpoints
+ if (type == Data) {
+ const ULONG size = 1u;
+ hr = ibp->SetDataParameters(size, DEBUG_BREAK_READ | DEBUG_BREAK_WRITE);
+ if (FAILED(hr)) {
+ const QString msg = QString::fromLatin1("Cannot set watch size to %1: %2").
+ arg(size).arg(CdbCore::msgComFailed("SetDataParameters", hr));
+ *errorMessage = msgCannotSetBreakpoint(expr, msg);
+ return false;
+ }
+ }
+ // Thread
+ if (threadId >= 0) {
+ hr = ibp->SetMatchThreadId(threadId);
+ if (FAILED(hr)) {
+ const QString msg = QString::fromLatin1("Cannot set thread id to %1: %2").
+ arg(threadId).arg(CdbCore::msgComFailed("SetMatchThreadId", hr));
+ *errorMessage = msgCannotSetBreakpoint(expr, msg);
+ return false;
+ }
+ }
+ // Flags
ULONG flags = 0;
if (enabled)
flags |= DEBUG_BREAKPOINT_ENABLED;
if (oneShot)
flags |= DEBUG_BREAKPOINT_ONE_SHOT;
- ibp->AddFlags(flags);
+ hr = ibp->AddFlags(flags);
+ if (FAILED(hr)) {
+ const QString msg = QString::fromLatin1("Cannot set flags to 0x%1: %2").
+ arg(flags, 0 ,16).arg(CdbCore::msgComFailed("AddFlags", hr));
+ *errorMessage = msgCannotSetBreakpoint(expr, msg);
+ return false;
+ }
return true;
}
*address = 0;
if (id)
*id = 0;
- HRESULT hr = debugControl->AddBreakpoint2(DEBUG_BREAKPOINT_CODE, DEBUG_ANY_ID, &ibp);
+ const ULONG iType = type == Code ? DEBUG_BREAKPOINT_CODE : DEBUG_BREAKPOINT_DATA;
+ HRESULT hr = debugControl->AddBreakpoint2(iType, DEBUG_ANY_ID, &ibp);
if (FAILED(hr)) {
*errorMessage = msgCannotAddBreakPoint(CdbCore::msgComFailed("AddBreakpoint2", hr));
return false;
normalizedFileNameCache()->clear();
}
+static inline QString msgCannotRetrieveBreakpoint(const QString &why)
+{
+ return QString::fromLatin1("Cannot retrieve breakpoint: %1").arg(why);
+}
+
+static inline int threadIdOfBreakpoint(CIDebugBreakpoint *ibp)
+{
+ // Thread: E_NOINTERFACE indicates no thread has been set.
+ int threadId = -1;
+ ULONG iThreadId;
+ if (S_OK == ibp->GetMatchThreadId(&iThreadId))
+ threadId = iThreadId;
+ return threadId;
+}
+
bool BreakPoint::retrieve(CIDebugBreakpoint *ibp, QString *errorMessage)
{
clear();
+ // Get type
+ ULONG iType;
+ ULONG processorType;
+ HRESULT hr = ibp->GetType(&iType, &processorType);
+ if (FAILED(hr)) {
+ *errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetType", hr));
+ return false;
+ }
+ type = iType == DEBUG_BREAKPOINT_CODE ? Code : Data;
+ // Get & parse expression
WCHAR wszBuf[MAX_PATH];
- const HRESULT hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
+ hr =ibp->GetOffsetExpressionWide(wszBuf, MAX_PATH, 0);
if (FAILED(hr)) {
- *errorMessage = QString::fromLatin1("Cannot retrieve breakpoint: %1").
- arg(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
+ *errorMessage = msgCannotRetrieveBreakpoint(CdbCore::msgComFailed("GetOffsetExpressionWide", hr));
return false;
}
+ threadId = threadIdOfBreakpoint(ibp);
// Pass Count is ignoreCount + 1
ibp->GetPassCount(&ignoreCount);
if (ignoreCount)
return true;
}
+// Change thread-id of a breakpoint
+static inline QString msgCannotSetBreakPointThread(unsigned long id, int tid, const QString &why)
+{
+ return QString::fromLatin1("Cannot set breakpoint %1 thread to %2: %3").arg(id).arg(tid).arg(why);
+}
+
+bool BreakPoint::setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage)
+{
+ if (debugBP)
+ qDebug() << Q_FUNC_INFO << id << threadId;
+ CIDebugBreakpoint *ibp = breakPointById(ctl, id, errorMessage);
+ if (!ibp) {
+ *errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage);
+ return false;
+ }
+ // Compare thread ids
+ const int oldThreadId = threadIdOfBreakpoint(ibp);
+ if (oldThreadId == threadId)
+ return true;
+ const ULONG newIThreadId = threadId == -1 ? DEBUG_ANY_ID : static_cast<ULONG>(threadId);
+ if (debugBP)
+ qDebug() << "Changing thread id of " << id << " from " << oldThreadId << " to " << threadId
+ << '(' << newIThreadId << ')';
+ const HRESULT hr = ibp->SetMatchThreadId(newIThreadId);
+ if (FAILED(hr)) {
+ *errorMessage = msgCannotSetBreakPointThread(id, threadId, *errorMessage);
+ return false;
+ }
+ return true;
}
+} // namespace CdbCore
struct BreakPoint
{
- BreakPoint();
+ enum Type { Code, // Stop in code.
+ Data // Stop when accessing address.
+ };
- int compare(const BreakPoint& rhs) const;
+ BreakPoint();
void clear();
void clearExpressionData();
QString expression() const;
- // Apply parameters
+ // Apply parameters (with the exception of type, which is
+ // passed as a parameter to IDebugControl within add().
bool apply(IDebugBreakpoint2 *ibp, QString *errorMessage) const;
- // Convenience to add to a IDebugControl4
+ // Convenience to add to a IDebugControl4.
bool add(CIDebugControl* debugControl,
QString *errorMessage,
unsigned long *id = 0,
static IDebugBreakpoint2 *breakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage);
static bool removeBreakPointById(CIDebugControl *ctl, unsigned long id, QString *errorMessage);
static bool setBreakPointEnabledById(CIDebugControl *ctl, unsigned long id, bool enabled, QString *errorMessage);
+ static bool setBreakPointThreadById(CIDebugControl *ctl, unsigned long id, int threadId, QString *errorMessage);
// Return a 'canonical' file (using '/' and capitalized drive letter)
static QString normalizeFileName(const QString &f);
static void clearNormalizeFileNameCache();
+ QString toString() const;
+ Type type;
QString fileName; // short name of source file
int lineNumber; // line in source file
QString funcName; // name of containing function
quint64 address;
+ int threadId;
QString condition; // condition associated with breakpoint
unsigned long ignoreCount; // ignore count associated with breakpoint
bool oneShot;
QDebug operator<<(QDebug, const BreakPoint &bp);
-inline bool operator==(const BreakPoint& b1, const BreakPoint& b2)
- { return b1.compare(b2) == 0; }
-inline bool operator!=(const BreakPoint& b1, const BreakPoint& b2)
- { return b1.compare(b2) != 0; }
-}
+} // namespace CdbCore
#endif // CDBCOREBREAKPOINTS_H
enum { debugBP = 0 };
+// Convert breakpoint structs
+CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd)
+{
+ CdbCore::BreakPoint rc;
+ rc.type = bpd.type == Debugger::Internal::BreakpointData::BreakpointType ?
+ CdbCore::BreakPoint::Code : CdbCore::BreakPoint::Data;
+
+ if (rc.type == CdbCore::BreakPoint::Data) {
+ QByteArray addressBA = bpd.address;
+ if (addressBA.startsWith("0x"))
+ addressBA.remove(0, 2);
+ bool ok;
+ rc.address = addressBA.toULongLong(&ok, 16);
+ if (!ok)
+ qWarning("Cdb: Cannot convert watchpoint address '%s'", bpd.address.constData());
+ }
+ if (!bpd.threadSpec.isEmpty()) {
+ bool ok;
+ rc.threadId = bpd.threadSpec.toInt(&ok);
+ if (!ok)
+ qWarning("Cdb: Cannot convert breakpoint thread specification '%s'", bpd.address.constData());
+ }
+ rc.fileName = QDir::toNativeSeparators(bpd.fileName);
+ rc.condition = bpd.condition;
+ rc.funcName = bpd.funcName;
+ rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt();
+ rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt();
+ rc.oneShot = false;
+ rc.enabled = bpd.enabled;
+ return rc;
+}
static inline QString msgCannotSetBreakAtFunction(const QString &func, const QString &why)
{
breakPointOk = ncdbbp.add(debugControl, &warning, &id, &address);
if (breakPointOk) {
if (debugBP)
- qDebug() << "Added " << id << " at " << address << ncdbbp;
+ qDebug("Added %lu at 0x%lx %s", id, address, qPrintable(ncdbbp.toString()));
handler->takeInsertedBreakPoint(nbd);
updateMarkers = true;
nbd->pending = false;
// Take over rest as is
nbd->bpCondition = nbd->condition;
nbd->bpIgnoreCount = nbd->ignoreCount;
+ nbd->bpThreadSpec = nbd->threadSpec;
nbd->bpFileName = nbd->fileName;
nbd->bpLineNumber = nbd->lineNumber;
nbd->bpFuncName = nbd->funcName;
foreach (BreakpointData *dbd, handler->takeDisabledBreakpoints())
if (!CdbCore::BreakPoint::setBreakPointEnabledById(debugControl, dbd->bpNumber.toUInt(), false, &warning))
warnings->push_back(warning);
+ // Check for modified thread ids.
+ for (int i = handler->size() - 1; i >= 0; i--) {
+ BreakpointData *bpd = handler->at(i);
+ if (bpd->threadSpec != bpd->bpThreadSpec) {
+ const int newThreadSpec = bpd->threadSpec.isEmpty() ? -1 : bpd->threadSpec.toInt();
+ if (CdbCore::BreakPoint::setBreakPointThreadById(debugControl, bpd->bpNumber.toUInt(), newThreadSpec, errorMessage)) {
+ bpd->bpThreadSpec = bpd->threadSpec;
+ } else {
+ qWarning("%s", qPrintable(*errorMessage));
+ }
+ }
+ }
if (updateMarkers)
handler->updateMarkers();
if (debugBP > 1) {
QList<CdbCore::BreakPoint> bps;
CdbCore::BreakPoint::getBreakPoints(debugControl, &bps, errorMessage);
- qDebug().nospace() << "### Breakpoints in engine: " << bps;
+ QDebug nsp = qDebug().nospace();
+ const int count = bps.size();
+ nsp <<"### Breakpoints in engine: " << count << '\n';
+ for (int i = 0; i < count; i++)
+ nsp << " #" << i << ' ' << bps.at(i) << '\n';
}
return true;
}
class QDebug;
QT_END_NAMESPACE
-// Convert breakpoint structs
-inline CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd)
-{
- CdbCore::BreakPoint rc;
- rc.fileName = QDir::toNativeSeparators(bpd.fileName);
- rc.condition = bpd.condition;
- rc.funcName = bpd.funcName;
- rc.ignoreCount = bpd.ignoreCount.isEmpty() ? 0 : bpd.ignoreCount.toInt();
- rc.lineNumber = bpd.lineNumber.isEmpty() ? -1 : bpd.lineNumber.toInt();
- rc.oneShot = false;
- rc.enabled = bpd.enabled;
- return rc;
-}
-
namespace Debugger {
namespace Internal {
+// Convert breakpoint structs
+CdbCore::BreakPoint breakPointFromBreakPointData(const Debugger::Internal::BreakpointData &bpd);
+
// Synchronize (halted) engine with BreakHandler.
bool synchronizeBreakPoints(CIDebugControl* ctl, CIDebugSymbols *syms,
BreakHandler *bh,
#include "cdboptionspage.h"
#include "cdboptions.h"
#include "cdbexceptionutils.h"
+#include "cdbsymbolpathlisteditor.h"
#include "debuggeragents.h"
#include "debuggeruiswitcher.h"
#include "debuggermainwindow.h"
#include "breakhandler.h"
#include "stackhandler.h"
#include "watchhandler.h"
+#include "threadshandler.h"
#include "registerhandler.h"
#include "moduleshandler.h"
#include "watchutils.h"
#include <utils/fancymainwindow.h>
#include <texteditor/itexteditor.h>
#include <utils/savedaction.h>
+#include <utils/checkablemessagebox.h>
#include <QtCore/QDebug>
#include <QtCore/QTimer>
}
}
+void CdbDebugEngine::startupChecks()
+{
+ // Check symbol server unless the user has an external/internal setup
+ if (!qgetenv("_NT_SYMBOL_PATH").isEmpty()
+ || CdbOptions::indexOfSymbolServerPath(m_d->m_options->symbolPaths) != -1)
+ return;
+ // Prompt to use Symbol server unless the user checked "No nagging".
+ Core::ICore *core = Core::ICore::instance();
+ const QString nagSymbolServerKey = CdbOptions::settingsGroup() + QLatin1String("/NoPromptSymbolServer");
+ bool noFurtherNagging = core->settings()->value(nagSymbolServerKey, false).toBool();
+ if (noFurtherNagging)
+ return;
+
+ const QString symServUrl = QLatin1String("http://support.microsoft.com/kb/311503");
+ const QString msg = tr("<html><head/><body><p>The debugger is not configured to use the public "
+ "<a href=\"%1\">Microsoft Symbol Server</a>. This is recommended "
+ "for retrieval of the symbols of the operating system libraries.</p>"
+ "<p><i>Note:</i> A fast internet connection is required for this to work smoothly. Also, a delay "
+ "might occur when connecting for the first time.</p>"
+ "<p>Would you like to set it up?</p></br>"
+ "</body></html>").arg(symServUrl);
+ const QDialogButtonBox::StandardButton answer =
+ Utils::CheckableMessageBox::question(core->mainWindow(), tr("Symbol Server"), msg,
+ tr("Do not ask again"), &noFurtherNagging);
+ core->settings()->setValue(nagSymbolServerKey, noFurtherNagging);
+ if (answer == QDialogButtonBox::No)
+ return;
+ // Prompt for path and add it. Synchronize QSetting and debugger.
+ const QString cacheDir = CdbSymbolPathListEditor::promptCacheDirectory(core->mainWindow());
+ if (cacheDir.isEmpty())
+ return;
+ m_d->m_options->symbolPaths.push_back(CdbOptions::symbolServerPath(cacheDir));
+ m_d->m_options->toSettings(core->settings());
+ syncDebuggerPaths();
+}
+
void CdbDebugEngine::startDebugger(const QSharedPointer<DebuggerStartParameters> &sp)
{
if (debugCDBExecution)
qDebug() << "startDebugger" << *sp;
CdbCore::BreakPoint::clearNormalizeFileNameCache();
+ startupChecks();
setState(AdapterStarting, Q_FUNC_INFO, __LINE__);
m_d->checkVersion();
if (m_d->m_hDebuggeeProcess) {
}
switch (sp->startMode) {
case AttachCore:
- case StartRemote:
+ case AttachToRemote:
warning(QLatin1String("Internal error: Mode not supported."));
setState(AdapterStartFailed, Q_FUNC_INFO, __LINE__);
emit startFailed();
m_d->clearDisplay();
m_d->m_inferiorStartupComplete = false;
setState(AdapterStarted, Q_FUNC_INFO, __LINE__);
-
+ // Options
+ QString errorMessage;
+ if (!m_d->setBreakOnThrow(theDebuggerBoolSetting(BreakOnThrow), &errorMessage))
+ manager()->showDebuggerOutput(LogWarning, errorMessage);
m_d->setVerboseSymbolLoading(m_d->m_options->verboseSymbolLoading);
// Figure out dumper. @TODO: same in gdb...
const QString dumperLibName = QDir::toNativeSeparators(manager()->qtDumperLibraryName());
setState(InferiorStarting, Q_FUNC_INFO, __LINE__);
manager()->showStatusMessage("Starting Debugger", messageTimeOut);
- QString errorMessage;
bool rc = false;
bool needWatchTimer = false;
m_d->clearForRun();
m_d->m_ignoreInitialBreakPoint = true;
// Launch console stub and wait for its startup
m_d->m_consoleStubProc.stop(); // We leave the console open, so recycle it now.
- m_d->m_consoleStubProc.setWorkingDirectory(sp->workingDir);
+ m_d->m_consoleStubProc.setWorkingDirectory(sp->workingDirectory);
m_d->m_consoleStubProc.setEnvironment(sp->environment);
rc = m_d->m_consoleStubProc.start(sp->executable, sp->processArgs);
if (!rc)
// continues in slotConsoleStubStarted()...
} else {
needWatchTimer = true;
- rc = m_d->startDebuggerWithExecutable(sp->workingDir,
+ rc = m_d->startDebuggerWithExecutable(sp->workingDirectory,
sp->executable,
sp->processArgs,
sp->environment,
qDebug() << Q_FUNC_INFO;
}
-QList<Symbol> CdbDebugEngine::moduleSymbols(const QString &moduleName)
+void CdbDebugEngine::requestModuleSymbols(const QString &moduleName)
{
QList<Symbol> rc;
QString errorMessage;
} while (false);
if (!success)
warning(errorMessage);
- return rc;
+ manager()->showModuleSymbols(moduleName, rc);
}
void CdbDebugEngine::reloadRegisters()
expression.clear();
}
if (!expression.isEmpty())
- m_stoppedMessage = CdbDebugEngine::tr("Breakpoint: %1").arg(expression);
+ m_stoppedMessage = breakpoint.type == CdbCore::BreakPoint::Code ?
+ CdbDebugEngine::tr("Breakpoint: %1").arg(expression) :
+ CdbDebugEngine::tr("Watchpoint: %1").arg(expression);
}
void CdbDebugEngine::reloadSourceFiles()
unsigned CdbDebugEngine::debuggerCapabilities() const
{
- return DisassemblerCapability | RegisterCapability | ShowMemoryCapability;
+ return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
+ |WatchpointCapability
+ |BreakOnThrowAndCatchCapability; // Sort-of: Can break on throw().
}
// Accessed by DebuggerManager
virtual void reloadModules();
virtual void loadSymbols(const QString &moduleName);
virtual void loadAllSymbols();
- virtual QList<Symbol> moduleSymbols(const QString &moduleName);
+ virtual void requestModuleSymbols(const QString &moduleName);
virtual void reloadRegisters();
virtual void reloadSourceFiles();
void warning(const QString &w);
private:
+ void startupChecks();
void setState(DebuggerState state, const char *func, int line);
inline bool startAttachDebugger(qint64 pid, DebuggerStartMode sm, QString *errorMessage);
void processTerminated(unsigned long exitCode);
{
}
+QString CdbOptions::settingsGroup()
+{
+ return QLatin1String(settingsGroupC);
+}
+
void CdbOptions::clear()
{
enabled = false;
return rc;
}
+static const char symbolServerPrefixC[] = "symsrv*symsrv.dll*";
+static const char symbolServerPostfixC[] = "*http://msdl.microsoft.com/download/symbols";
+
+QString CdbOptions::symbolServerPath(const QString &cacheDir)
+{
+ QString s = QLatin1String(symbolServerPrefixC);
+ s += QDir::toNativeSeparators(cacheDir);
+ s += QLatin1String(symbolServerPostfixC);
+ return s;
+}
+
+bool CdbOptions::isSymbolServerPath(const QString &path, QString *cacheDir /* = 0 */)
+{
+ // Split apart symbol server post/prefixes
+ if (!path.startsWith(QLatin1String(symbolServerPrefixC)) || !path.endsWith(QLatin1String(symbolServerPostfixC)))
+ return false;
+ if (cacheDir) {
+ const unsigned prefixLength = qstrlen(symbolServerPrefixC);
+ *cacheDir = path.mid(prefixLength, path.size() - prefixLength - qstrlen(symbolServerPostfixC));
+ }
+ return true;
+}
+
+int CdbOptions::indexOfSymbolServerPath(const QStringList &paths, QString *cacheDir /* = 0 */)
+{
+ const int count = paths.size();
+ for (int i = 0; i < count; i++)
+ if (CdbOptions::isSymbolServerPath(paths.at(i), cacheDir))
+ return i;
+ return -1;
+}
+
} // namespace Internal
} // namespace Debugger
SymbolOptionsChanged = 0x4 };
unsigned compare(const CdbOptions &s) const;
+ // Format a symbol server specification with a cache directory
+ static QString symbolServerPath(const QString &cacheDir);
+ // Check whether the path is a symbol server specification and return the cache directory
+ static bool isSymbolServerPath(const QString &symbolPath, QString *cacheDir = 0);
+ static int indexOfSymbolServerPath(const QStringList &symbolPaths, QString *cacheDir = 0);
+
+ static QString settingsGroup();
+
bool enabled;
QString path;
QStringList symbolPaths;
#include "cdbdebugengine_p.h"
#include "debuggeractions.h"
#include "watchutils.h"
+#include "threadshandler.h"
#include <utils/savedaction.h>
const CdbCore::StackFrame &coreFrame = it.value();
data.address = coreFrame.address;
data.function = coreFrame.function;
- data.line = coreFrame.line;
+ data.lineNumber = coreFrame.line;
// Basename only for brevity
const int slashPos = coreFrame.fileName.lastIndexOf(slash);
- data.file = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
+ data.fileName = slashPos == -1 ? coreFrame.fileName : coreFrame.fileName.mid(slashPos + 1);
threads->push_back(data);
}
return true;
**************************************************************************/
#include "cdbsymbolpathlisteditor.h"
+#include "cdboptions.h"
+#include <utils/pathchooser.h>
+
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
#include <QtGui/QFileDialog>
#include <QtGui/QAction>
+#include <QtGui/QDialogButtonBox>
+#include <QtGui/QVBoxLayout>
+#include <QtGui/QFormLayout>
+#include <QtGui/QMessageBox>
namespace Debugger {
namespace Internal {
+CacheDirectoryDialog::CacheDirectoryDialog(QWidget *parent) :
+ QDialog(parent), m_chooser(new Utils::PathChooser),
+ m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel))
+{
+ setWindowTitle(tr("Select Local Cache Folder"));
+ setModal(true);
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+
+ QFormLayout *formLayout = new QFormLayout;
+ m_chooser->setExpectedKind(Utils::PathChooser::Directory);
+ m_chooser->setMinimumWidth(400);
+ formLayout->addRow(tr("Path:"), m_chooser);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addLayout(formLayout);
+ mainLayout->addWidget(m_buttonBox);
+
+ setLayout(mainLayout);
+
+ connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+}
+
+void CacheDirectoryDialog::setPath(const QString &p)
+{
+ m_chooser->setPath(p);
+}
+
+QString CacheDirectoryDialog::path() const
+{
+ return m_chooser->path();
+}
+
+void CacheDirectoryDialog::accept()
+{
+ // Ensure path exists
+ QString cache = path();
+ if (cache.isEmpty())
+ return;
+ QFileInfo fi(cache);
+ // Folder exists - all happy.
+ if (fi.isDir()) {
+ QDialog::accept();
+ return;
+ }
+ // Does a file of the same name exist?
+ if (fi.exists()) {
+ QMessageBox::warning(this, tr("Already Exists"),
+ tr("A file named '%1' already exists.").arg(cache));
+ return;
+ }
+ // Create
+ QDir root(QDir::root());
+ if (!root.mkpath(cache)) {
+ QMessageBox::warning(this, tr("Cannot Create"),
+ tr("The folder '%1' could not be created.").arg(cache));
+ return;
+ }
+ QDialog::accept();
+}
+
CdbSymbolPathListEditor::CdbSymbolPathListEditor(QWidget *parent) :
Utils::PathListEditor(parent)
{
"Requires specifying a local cache directory."));
}
+QString CdbSymbolPathListEditor::promptCacheDirectory(QWidget *parent)
+{
+ CacheDirectoryDialog dialog(parent);
+ dialog.setPath(QDir::tempPath() + QDir::separator() + QLatin1String("symbolcache"));
+ if (dialog.exec() != QDialog::Accepted)
+ return QString();
+ return dialog.path();
+}
+
void CdbSymbolPathListEditor::addSymbolServer()
{
- const QString title = tr("Pick a local cache directory");
- const QString cacheDir = QFileDialog::getExistingDirectory(this, title);
- if (!cacheDir.isEmpty()) {
- const QString path = QString::fromLatin1("symsrv*symsrv.dll*%1*http://msdl.microsoft.com/download/symbols").
- arg(QDir::toNativeSeparators(cacheDir));
- insertPathAtCursor(path);
- }
+ const QString cacheDir = promptCacheDirectory(this);
+ if (!cacheDir.isEmpty())
+ insertPathAtCursor(CdbOptions::symbolServerPath(cacheDir));
}
} // namespace Internal
#include <utils/pathlisteditor.h>
+#include <QtGui/QDialog>
+
+namespace Utils {
+ class PathChooser;
+}
+
+QT_BEGIN_NAMESPACE
+class QDialogButtonBox;
+QT_END_NAMESPACE
+
namespace Debugger {
namespace Internal {
+// Internal helper dialog prompting for a cache directory
+// using a PathChooser.
+// Note that QFileDialog does not offer a way of suggesting
+// a non-existent folder, which is in turn automatically
+// created. This is done here (suggest $TEMP\symbolcache
+// regardless of its existence).
+
+class CacheDirectoryDialog : public QDialog {
+ Q_OBJECT
+public:
+ explicit CacheDirectoryDialog(QWidget *parent = 0);
+
+ void setPath(const QString &p);
+ QString path() const;
+
+ virtual void accept();
+
+private:
+ Utils::PathChooser *m_chooser;
+ QDialogButtonBox *m_buttonBox;
+};
+
class CdbSymbolPathListEditor : public Utils::PathListEditor
{
Q_OBJECT
public:
explicit CdbSymbolPathListEditor(QWidget *parent = 0);
+ static QString promptCacheDirectory(QWidget *parent);
+
private slots:
void addSymbolServer();
};
m_debugEventCallback->setModuleCount(m);
}
+static inline const char *debugFilterDescription(ULONG in)
+{
+ switch (in) {
+ case DEBUG_FILTER_BREAK:
+ return "break";
+ case DEBUG_FILTER_SECOND_CHANCE_BREAK:
+ return "2nd-chance-break";
+ case DEBUG_FILTER_OUTPUT:
+ return "output";
+ case DEBUG_FILTER_IGNORE:
+ return "ignore";
+ default:
+ break;
+ }
+ return "unknown";
+}
+
+static inline QString msgCannotChangeExceptionCommands(unsigned long code, const QString &why)
+{
+ return QString::fromLatin1("Cannot change exception commands for 0x%1: %2").
+ arg(code, 0, 16).arg(why);
+}
+
+bool CoreEngine::setBreakOnThrow(bool b, QString *errorMessage)
+{
+ // See eventFilterStatus() for defaults
+ const unsigned long code = 0xe06d7363;
+ const unsigned long executionCommand = b ? DEBUG_FILTER_BREAK : DEBUG_FILTER_SECOND_CHANCE_BREAK;
+ const unsigned long continueCommand = b ? DEBUG_FILTER_BREAK : DEBUG_FILTER_SECOND_CHANCE_BREAK;
+ return setExceptionCommands(code, executionCommand, continueCommand, errorMessage);
+}
+
+bool CoreEngine::setExceptionCommands(ULONG code,
+ ULONG executionCommand,
+ ULONG continueCommand,
+ QString *errorMessage)
+{
+ DEBUG_EXCEPTION_FILTER_PARAMETERS exceptionParameters;
+ HRESULT hr = m_cif.debugControl->GetExceptionFilterParameters(1, &code, 0, &exceptionParameters);
+ if (FAILED(hr)) {
+ *errorMessage = msgCannotChangeExceptionCommands(code, msgComFailed("GetExceptionFilterParameters", hr));
+ return false;
+ }
+ if (exceptionParameters.ExecutionOption == executionCommand
+ && exceptionParameters.ContinueOption == continueCommand)
+ return true;
+ if (debug)
+ qDebug("Changing exception commands of 0x%x from %s/%s to %s/%s",
+ code,
+ debugFilterDescription(exceptionParameters.ExecutionOption),
+ debugFilterDescription(exceptionParameters.ContinueOption),
+ debugFilterDescription(executionCommand),
+ debugFilterDescription(continueCommand));
+
+ exceptionParameters.ExecutionOption = executionCommand;
+ exceptionParameters.ContinueOption = continueCommand;
+ hr = m_cif.debugControl->SetExceptionFilterParameters(1, &exceptionParameters);
+ if (FAILED(hr)) {
+ *errorMessage = msgCannotChangeExceptionCommands(code, msgComFailed("SetExceptionFilterParameters", hr));
+ return false;
+ }
+ return true;
+}
+
+static void formatEventFilter(CIDebugControl *ctl, unsigned long start, unsigned long end,
+ bool isException,
+ QTextStream &str)
+{
+ enum { bufSize =2048 };
+ WCHAR buffer[bufSize];
+ for (unsigned long i = start; i < end; i++) {
+ HRESULT hr = ctl->GetEventFilterTextWide(i, buffer, bufSize, 0);
+ if (SUCCEEDED(hr)) {
+ ULONG size;
+ str << "- #" << i << " \"" << QString::fromUtf16(buffer) << '"';
+ hr = ctl->GetEventFilterCommandWide(i, buffer, bufSize, &size);
+ if (SUCCEEDED(hr) && size > 1)
+ str << " command: '" << QString::fromUtf16(buffer) << '\'';
+ if (isException) {
+ DEBUG_EXCEPTION_FILTER_PARAMETERS exceptionParameters;
+ hr = ctl->GetExceptionFilterParameters(1, 0, i, &exceptionParameters);
+ if (SUCCEEDED(hr)) {
+ str.setIntegerBase(16);
+ str << " code: 0x" << exceptionParameters.ExceptionCode;
+ str.setIntegerBase(10);
+ str << " execute: '"
+ << debugFilterDescription(exceptionParameters.ExecutionOption)
+ << "' continue: '" << debugFilterDescription(exceptionParameters.ContinueOption)
+ << '\'';
+ if (exceptionParameters.SecondCommandSize) {
+ hr = ctl->GetExceptionFilterSecondCommandWide(i, buffer, bufSize, 0);
+ if (SUCCEEDED(hr))
+ str << " 2nd-command '" << QString::fromUtf16(buffer) << '\'';
+ }
+ }
+ } // isException
+ str << '\n';
+ }
+ }
+}
+
+QString CoreEngine::eventFilterStatus() const
+{
+ ULONG specificEvents, specificExceptions, arbitraryExceptions;
+ QString rc;
+ QTextStream str(&rc);
+
+ HRESULT hr = m_cif.debugControl->GetNumberEventFilters(&specificEvents, &specificExceptions, &arbitraryExceptions);
+ if (FAILED(hr))
+ return QString();
+ str << "Specific events\n";
+ formatEventFilter(m_cif.debugControl, 0, specificEvents, false, str);
+ const ULONG arbitraryExceptionsStart = specificEvents + specificExceptions;
+ str << "Specific exceptions\n";
+ formatEventFilter(m_cif.debugControl, specificEvents, arbitraryExceptionsStart, true, str);
+ str << "Arbitrary exceptions\n";
+ const ULONG arbitraryExceptionsEnd = arbitraryExceptionsStart + arbitraryExceptions;
+ formatEventFilter(m_cif.debugControl, arbitraryExceptionsStart, arbitraryExceptionsEnd, true, str);
+ return rc;
+}
+
// ------------- DEBUG_VALUE formatting helpers
// format an array of integers as "0x323, 0x2322, ..."
unsigned moduleCount() const;
+ bool setBreakOnThrow(bool b, QString *errorMessage);
+ bool setExceptionCommands(ULONG code,
+ ULONG executionCommand,
+ ULONG continueCommand,
+ QString *errorMessage);
+
+ QString eventFilterStatus() const;
+
signals:
void watchTimerDebugEvent();
script
HEADERS += breakhandler.h \
breakwindow.h \
+ breakpoint.h \
debuggeragents.h \
debuggeractions.h \
debuggerconstants.h \
procinterrupt.h \
registerhandler.h \
registerwindow.h \
+ stackframe.h \
stackhandler.h \
stackwindow.h \
snapshothandler.h \
watchwindow.h \
name_demangler.h \
debuggeruiswitcher.h \
- debuggermainwindow.h
+ debuggermainwindow.h \
+ threadshandler.h
SOURCES += breakhandler.cpp \
breakwindow.cpp \
breakwindow.h \
watchutils.cpp \
name_demangler.cpp \
debuggeruiswitcher.cpp \
- debuggermainwindow.cpp
+ debuggermainwindow.cpp \
+ threadshandler.cpp \
+ stackframe.cpp
FORMS += attachexternaldialog.ui \
attachcoredialog.ui \
+ attachtcfdialog.ui \
breakbyfunction.ui \
breakcondition.ui \
dumperoptionpage.ui \
include(gdb/gdb.pri)
include(script/script.pri)
include(pdb/pdb.pri)
+include(tcf/tcf.pri)
include(shared/shared.pri)
OTHER_FILES += Debugger.pluginspec
<file>images/debugger_stepoverproc_small.png</file>
<file>images/debugger_stop.png</file>
<file>images/debugger_stop_small.png</file>
+ <file>images/watchpoint.png</file>
<file>images/breakpoint_16.png</file>
<file>images/breakpoint_24.png</file>
<file>images/breakpoint_disabled_16.png</file>
StartExternal, // Start binary found in file system
AttachExternal, // Attach to running process by process id
AttachCrashedExternal, // Attach to crashed process by process id
+ AttachTcf, // Attach to a running Target Communication Framework agent
AttachCore, // Attach to a core file
- StartRemote // Start and attach to a remote process
+ AttachToRemote, // Start and attach to a remote process
+ StartRemoteGdb // Start gdb itself remotely
};
enum DebuggerCapabilities
BreakOnThrowAndCatchCapability = 0x200,
ReturnFromFunctionCapability = 0x400,
CreateFullBacktraceCapability = 0x800,
- AddWatcherCapability = 0x1000
+ AddWatcherCapability = 0x1000,
+ WatchpointCapability = 0x2000
};
enum LogChannel
#include "ui_attachcoredialog.h"
#include "ui_attachexternaldialog.h"
+#include "ui_attachtcfdialog.h"
#include "ui_startexternaldialog.h"
#include "ui_startremotedialog.h"
///////////////////////////////////////////////////////////////////////
//
+// AttachTcfDialog
+//
+///////////////////////////////////////////////////////////////////////
+
+AttachTcfDialog::AttachTcfDialog(QWidget *parent)
+ : QDialog(parent),
+ m_ui(new Ui::AttachTcfDialog)
+{
+ m_ui->setupUi(this);
+ m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+ m_ui->serverStartScript->setExpectedKind(Utils::PathChooser::File);
+ m_ui->serverStartScript->setPromptDialogTitle(tr("Select Executable"));
+
+ connect(m_ui->useServerStartScriptCheckBox, SIGNAL(toggled(bool)),
+ this, SLOT(updateState()));
+
+ connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
+ connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+ updateState();
+}
+
+AttachTcfDialog::~AttachTcfDialog()
+{
+ delete m_ui;
+}
+
+void AttachTcfDialog::setRemoteChannel(const QString &channel)
+{
+ m_ui->channelLineEdit->setText(channel);
+}
+
+QString AttachTcfDialog::remoteChannel() const
+{
+ return m_ui->channelLineEdit->text();
+}
+
+void AttachTcfDialog::setRemoteArchitectures(const QStringList &list)
+{
+ m_ui->architectureComboBox->clear();
+ if (!list.isEmpty()) {
+ m_ui->architectureComboBox->insertItems(0, list);
+ m_ui->architectureComboBox->setCurrentIndex(0);
+ }
+}
+
+void AttachTcfDialog::setRemoteArchitecture(const QString &arch)
+{
+ int index = m_ui->architectureComboBox->findText(arch);
+ if (index != -1)
+ m_ui->architectureComboBox->setCurrentIndex(index);
+}
+
+QString AttachTcfDialog::remoteArchitecture() const
+{
+ int index = m_ui->architectureComboBox->currentIndex();
+ return m_ui->architectureComboBox->itemText(index);
+}
+
+void AttachTcfDialog::setServerStartScript(const QString &scriptName)
+{
+ m_ui->serverStartScript->setPath(scriptName);
+}
+
+QString AttachTcfDialog::serverStartScript() const
+{
+ return m_ui->serverStartScript->path();
+}
+
+void AttachTcfDialog::setUseServerStartScript(bool on)
+{
+ m_ui->useServerStartScriptCheckBox->setChecked(on);
+}
+
+bool AttachTcfDialog::useServerStartScript() const
+{
+ return m_ui->useServerStartScriptCheckBox->isChecked();
+}
+
+void AttachTcfDialog::updateState()
+{
+ bool enabled = m_ui->useServerStartScriptCheckBox->isChecked();
+ m_ui->serverStartScriptLabel->setEnabled(enabled);
+ m_ui->serverStartScript->setEnabled(enabled);
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
// StartExternalDialog
//
///////////////////////////////////////////////////////////////////////
m_ui->execFile->setExpectedKind(Utils::PathChooser::File);
m_ui->execFile->setPromptDialogTitle(tr("Select Executable"));
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+ m_ui->workingDirectory->setExpectedKind(Utils::PathChooser::Directory);
+ m_ui->workingDirectory->setPromptDialogTitle(tr("Select Working Directory"));
//execLabel->setHidden(false);
//execEdit->setHidden(false);
return m_ui->execFile->path();
}
+void StartExternalDialog::setWorkingDirectory(const QString &str)
+{
+ m_ui->workingDirectory->setPath(str);
+}
+
+QString StartExternalDialog::workingDirectory() const
+{
+ return m_ui->workingDirectory->path();
+}
+
void StartExternalDialog::setExecutableArguments(const QString &str)
{
m_ui->argsEdit->setText(str);
namespace Ui {
class AttachCoreDialog;
class AttachExternalDialog;
+class AttachTcfDialog;
class StartExternalDialog;
class StartRemoteDialog;
} // namespace Ui
ProcessListFilterModel *m_model;
};
+class AttachTcfDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AttachTcfDialog(QWidget *parent);
+ ~AttachTcfDialog();
+
+ QString remoteChannel() const;
+ void setRemoteChannel(const QString &host);
+
+ QString remoteArchitecture() const;
+ void setRemoteArchitecture(const QString &arch);
+ void setRemoteArchitectures(const QStringList &arches);
+
+ QString serverStartScript() const;
+ bool useServerStartScript() const;
+ void setUseServerStartScript(bool on);
+ void setServerStartScript(const QString &scriptName);
+
+private slots:
+ void updateState();
+
+private:
+ Ui::AttachTcfDialog *m_ui;
+};
+
class StartExternalDialog : public QDialog
{
Q_OBJECT
explicit StartExternalDialog(QWidget *parent);
~StartExternalDialog();
+ QString executableFile() const;
void setExecutableFile(const QString &executable);
- void setExecutableArguments(const QString &args);
- QString executableFile() const;
QString executableArguments() const;
+ void setExecutableArguments(const QString &args);
+
+ QString workingDirectory() const;
+ void setWorkingDirectory(const QString &str);
+
bool breakAtMain() const;
private:
explicit StartRemoteDialog(QWidget *parent);
~StartRemoteDialog();
- void setRemoteChannel(const QString &host);
- void setRemoteArchitecture(const QString &arch);
- void setRemoteArchitectures(const QStringList &arches);
- void setLocalExecutable(const QString &executable);
- void setDebugger(const QString &debugger);
QString localExecutable() const;
+ void setLocalExecutable(const QString &executable);
+
QString remoteChannel() const;
+ void setRemoteChannel(const QString &host);
+
QString remoteArchitecture() const;
- void setServerStartScript(const QString &scriptName);
- QString serverStartScript() const;
- void setUseServerStartScript(bool on);
+ void setRemoteArchitecture(const QString &arch);
+ void setRemoteArchitectures(const QStringList &arches);
+
bool useServerStartScript() const;
- void setSysRoot(const QString &sysRoot);
+ void setUseServerStartScript(bool on);
+ QString serverStartScript() const;
+ void setServerStartScript(const QString &scriptName);
+
QString sysRoot() const;
+ void setSysRoot(const QString &sysRoot);
+
QString debugger() const;
+ void setDebugger(const QString &debugger);
private slots:
void updateState();
#include "debuggermanager.h"
-#include "debuggerplugin.h"
#include "debuggeractions.h"
#include "debuggeragents.h"
-#include "debuggerrunner.h"
#include "debuggerconstants.h"
-#include "idebuggerengine.h"
+#include "debuggermainwindow.h"
+#include "debuggerplugin.h"
+#include "debuggerrunner.h"
#include "debuggerstringutils.h"
-#include "watchutils.h"
+#include "debuggertooltip.h"
#include "debuggeruiswitcher.h"
-#include "debuggermainwindow.h"
+#include "idebuggerengine.h"
+#include "watchutils.h"
#include "breakwindow.h"
#include "debuggeroutputwindow.h"
#include "snapshothandler.h"
#include "stackhandler.h"
#include "stackframe.h"
+#include "threadshandler.h"
#include "watchhandler.h"
#include "debuggerdialogs.h"
#include <QtGui/QTextCursor>
#include <QtGui/QToolButton>
#include <QtGui/QToolTip>
+#include <QtGui/QTreeWidget>
#define DEBUG_STATE 1
#ifdef DEBUG_STATE
IDebuggerEngine *createGdbEngine(DebuggerManager *parent);
IDebuggerEngine *createScriptEngine(DebuggerManager *parent);
IDebuggerEngine *createPdbEngine(DebuggerManager *parent);
+IDebuggerEngine *createTcfEngine(DebuggerManager *parent);
// The createCdbEngine function takes a list of options pages it can add to.
// This allows for having a "enabled" toggle on the page independently
nospace << "executable=" << p.executable << " coreFile=" << p.coreFile
<< " processArgs=" << p.processArgs.join(sep)
<< " environment=<" << p.environment.size() << " variables>"
- << " workingDir=" << p.workingDir << " buildDir=" << p.buildDir
+ << " workingDir=" << p.workingDirectory << " buildDir=" << p.buildDirectory
<< " attachPID=" << p.attachPID << " useTerminal=" << p.useTerminal
<< " remoteChannel=" << p.remoteChannel
<< " remoteArchitecture=" << p.remoteArchitecture
static Debugger::Internal::IDebuggerEngine *scriptEngine = 0;
static Debugger::Internal::IDebuggerEngine *cdbEngine = 0;
static Debugger::Internal::IDebuggerEngine *pdbEngine = 0;
+static Debugger::Internal::IDebuggerEngine *tcfEngine = 0;
struct DebuggerManagerPrivate
{
DebuggerManagerActions m_actions;
QWidget *m_breakWindow;
+ QWidget *m_returnWindow;
QWidget *m_localsWindow;
+ QWidget *m_watchersWindow;
QWidget *m_registerWindow;
QWidget *m_modulesWindow;
QWidget *m_snapshotWindow;
SourceFilesWindow *m_sourceFilesWindow;
QWidget *m_stackWindow;
QWidget *m_threadsWindow;
- QWidget *m_watchersWindow;
DebuggerOutputWindow *m_outputWindow;
bool m_busy;
doDelete(pdbEngine);
doDelete(gdbEngine);
doDelete(cdbEngine);
+ doDelete(tcfEngine);
doDelete(d->m_breakHandler);
doDelete(d->m_threadsHandler);
doDelete(gdbEngine);
doDelete(scriptEngine);
doDelete(cdbEngine);
+ doDelete(tcfEngine);
#undef doDelete
DebuggerManagerPrivate::instance = 0;
delete d;
d->m_sourceFilesWindow->setObjectName(QLatin1String("CppDebugSources"));
d->m_threadsWindow = new ThreadsWindow;
d->m_threadsWindow->setObjectName(QLatin1String("CppDebugThreads"));
+ d->m_returnWindow = new WatchWindow(WatchWindow::ReturnType, this);
+ d->m_returnWindow->setObjectName(QLatin1String("CppDebugReturn"));
d->m_localsWindow = new WatchWindow(WatchWindow::LocalsType, this);
d->m_localsWindow->setObjectName(QLatin1String("CppDebugLocals"));
d->m_watchersWindow = new WatchWindow(WatchWindow::WatchersType, this);
this, SLOT(loadAllSymbols()));
connect(modulesView, SIGNAL(fileOpenRequested(QString)),
this, SLOT(fileOpen(QString)));
- connect(modulesView, SIGNAL(newDockRequested(QWidget*)),
- this, SLOT(createNewDock(QWidget*)));
// Source Files
//d->m_sourceFilesHandler = new SourceFilesHandler;
d->m_registerHandler = new RegisterHandler;
registerView->setModel(d->m_registerHandler->model());
- // Locals
+
+ // Return Value
d->m_watchHandler = new WatchHandler(this);
+ QTreeView *returnView = qobject_cast<QTreeView *>(d->m_returnWindow);
+ returnView->setModel(d->m_watchHandler->model(ReturnWatch));
+
+ // Locals
QTreeView *localsView = qobject_cast<QTreeView *>(d->m_localsWindow);
localsView->setModel(d->m_watchHandler->model(LocalsWatch));
d->m_outputWindow, SLOT(showOutput(int, QString)), Qt::QueuedConnection);
// Tooltip
- //QTreeView *tooltipView = qobject_cast<QTreeView *>(d->m_tooltipWindow);
- //tooltipView->setModel(d->m_watchHandler->model(TooltipsWatch));
qRegisterMetaType<WatchData>("WatchData");
qRegisterMetaType<StackCookie>("StackCookie");
localsAndWatchers->setObjectName(QLatin1String("CppDebugLocalsAndWatchers"));
localsAndWatchers->setWindowTitle(d->m_localsWindow->windowTitle());
localsAndWatchers->addWidget(d->m_localsWindow);
+ localsAndWatchers->addWidget(d->m_returnWindow);
localsAndWatchers->addWidget(d->m_watchersWindow);
//localsAndWatchers->addWidget(d->m_tooltipWindow);
localsAndWatchers->setStretchFactor(0, 3);
//pdbEngine->addOptionPages(&rc);
}
+ if (enabledTypeFlags & TcfEngineType) {
+ tcfEngine = createTcfEngine(this);
+ tcfEngine->addOptionPages(&rc);
+ }
+
d->m_engine = 0;
STATE_DEBUG(gdbEngine << cdbEngine << scriptEngine
<< pdbEngine << rc.size());
d->m_snapshotHandler->removeSnapshot(index);
}
-BreakpointData *DebuggerManager::findBreakpoint(const QString &fileName, int lineNumber)
-{
- if (!d->m_breakHandler)
- return 0;
- int index = d->m_breakHandler->findBreakpoint(fileName, lineNumber);
- return index == -1 ? 0 : d->m_breakHandler->at(index);
-}
-
-// FIXME: move further up the plugin where there's more specific context
-// information available.
-static BreakpointData *createBreakpointByFileAndLine
- (const QString &fileName, int lineNumber, BreakHandler *handler)
-{
- BreakpointData *data = new BreakpointData(handler);
- if (lineNumber > 0) {
- data->fileName = fileName;
- data->lineNumber = QByteArray::number(lineNumber);
- data->pending = true;
- data->setMarkerFileName(fileName);
- data->setMarkerLineNumber(lineNumber);
- } else {
- data->funcName = fileName;
- data->lineNumber = 0;
- data->pending = true;
- // FIXME: Figure out in which disassembler view the Marker sits.
- // Might be better to let the user code create the BreakpointData
- // structure and insert it here.
- data->setMarkerFileName(QString());
- data->setMarkerLineNumber(0);
- }
- return data;
-}
-
-void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber)
-{
- STATE_DEBUG(fileName << lineNumber);
- QTC_ASSERT(d->m_breakHandler, return);
- if (state() != InferiorRunning
- && state() != InferiorStopped
- && state() != DebuggerNotReady) {
- showStatusMessage(tr("Changing breakpoint state requires either a "
- "fully running or fully stopped application."));
- return;
- }
-
- int index = d->m_breakHandler->findBreakpoint(fileName, lineNumber);
- if (index == -1)
- d->m_breakHandler->appendBreakpoint(
- createBreakpointByFileAndLine(fileName, lineNumber, d->m_breakHandler));
- else
- d->m_breakHandler->removeBreakpoint(index);
-
- attemptBreakpointSynchronization();
-}
-
void DebuggerManager::attemptBreakpointSynchronization()
{
if (d->m_engine)
// Debugger type for mode
static IDebuggerEngine *debuggerEngineForMode(DebuggerStartMode startMode, QString *errorMessage)
{
+ if (startMode == AttachTcf)
+ return tcfEngine;
+
#ifdef Q_OS_WIN
// Preferably Windows debugger for attaching locally.
- if (startMode != StartRemote && cdbEngine)
+ if (startMode != AttachToRemote && cdbEngine)
return cdbEngine;
if (gdbEngine)
return gdbEngine;
d->m_engine = debuggerEngineForToolChain(sp->toolChainType);
if (d->m_engine == 0
- && startMode != StartRemote
+ && startMode != AttachToRemote
&& !sp->executable.isEmpty())
d->m_engine = debuggerEngineForExecutable(
sp->executable, &errorMessage, &settingsIdHint);
d->m_sourceFilesWindow->removeAll();
d->m_disassemblerViewAgent.cleanup();
d->m_actions.reverseDirectionAction->setChecked(false);
+ hideDebuggerToolTip();
// FIXME: Move to plugin?
using namespace Core;
d->m_engine->loadSymbols(module);
}
-QList<Symbol> DebuggerManager::moduleSymbols(const QString &moduleName)
+void DebuggerManager::requestModuleSymbols(const QString &moduleName)
{
- QTC_ASSERT(d->m_engine, return QList<Symbol>());
- return d->m_engine->moduleSymbols(moduleName);
+ QTC_ASSERT(d->m_engine, return);
+ d->m_engine->requestModuleSymbols(moduleName);
+}
+
+void DebuggerManager::showModuleSymbols(const QString &moduleName,
+ const QList<Symbol> &symbols)
+{
+ QTC_ASSERT(d->m_engine, return);
+ QTreeWidget *w = new QTreeWidget;
+ w->setColumnCount(3);
+ w->setRootIsDecorated(false);
+ w->setAlternatingRowColors(true);
+ w->setSortingEnabled(true);
+ w->setHeaderLabels(QStringList() << tr("Symbol") << tr("Address") << tr("Code"));
+ w->setWindowTitle(tr("Symbols in \"%1\"").arg(moduleName));
+ foreach (const Symbol &s, symbols) {
+ QTreeWidgetItem *it = new QTreeWidgetItem;
+ it->setData(0, Qt::DisplayRole, s.name);
+ it->setData(1, Qt::DisplayRole, s.address);
+ it->setData(2, Qt::DisplayRole, s.state);
+ w->addTopLevelItem(it);
+ }
+ createNewDock(w);
}
void DebuggerManager::executeStep()
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
d->m_breakWindow->setCursor(cursor);
+ d->m_returnWindow->setCursor(cursor);
d->m_localsWindow->setCursor(cursor);
d->m_modulesWindow->setCursor(cursor);
d->m_outputWindow->setCursor(cursor);
dialog.setDetailedText(details);
dialog.exec();
if (dialog.clickedButton() == qtPref) {
- Core::ICore::instance()->showOptionsDialog(_(Qt4ProjectManager::Constants::QT_SETTINGS_CATEGORY),
- _(Qt4ProjectManager::Constants::QTVERSION_SETTINGS_PAGE_ID));
+ Core::ICore::instance()->showOptionsDialog(
+ _(Qt4ProjectManager::Constants::QT_SETTINGS_CATEGORY),
+ _(Qt4ProjectManager::Constants::QTVERSION_SETTINGS_PAGE_ID));
} else if (dialog.clickedButton() == helperOff) {
- theDebuggerAction(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false);
+ theDebuggerAction(UseDebuggingHelpers)
+ ->setValue(qVariantFromValue(false), false);
}
}
void DebuggerManager::fontSettingsChanged(const TextEditor::FontSettings &settings)
{
int size = settings.fontZoom() * settings.fontSize() / 100;
+ changeFontSize(d->m_returnWindow, size);
changeFontSize(d->m_localsWindow, size);
changeFontSize(d->m_watchersWindow, size);
changeFontSize(d->m_breakWindow, size);
{
d->m_watchersWindow->setVisible(
d->m_watchHandler->model(WatchersWatch)->rowCount(QModelIndex()) > 0);
+ d->m_returnWindow->setVisible(
+ d->m_watchHandler->model(ReturnWatch)->rowCount(QModelIndex()) > 0);
}
void DebuggerManager::openTextEditor(const QString &titlePattern,
{
d->m_startParameters->executable = fileName;
d->m_startParameters->processArgs = QStringList() << "--run-debuggee";
- d->m_startParameters->workingDir.clear();
+ d->m_startParameters->workingDirectory.clear();
//startNewDebugger(StartInternal);
}
#include "debugger_global.h"
#include "debuggerconstants.h"
+#include <coreplugin/ssh/sshconnection.h>
+
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <QtCore/QStringList>
class CdbDumperInitThread;
class CdbExceptionLoggerEventCallback;
class GdbEngine;
+class TcfEngine;
class CdbDebugEngine;
class CdbDebugEnginePrivate;
class TrkGdbAdapter;
QString coreFile;
QStringList processArgs;
QStringList environment;
- QString workingDir;
- QString buildDir;
+ QString workingDirectory;
+ QString buildDirectory;
qint64 attachPID;
bool useTerminal;
QString crashParameter; // for AttachCrashedExternal
QString sysRoot;
QString debuggerCommand;
int toolChainType;
- QString remoteDumperLib;
+ QByteArray remoteDumperLib;
QString qtInstallPath;
QString dumperLibrary;
QStringList dumperLibraryLocations;
+ Core::SshServerInfo sshserver;
DebuggerStartMode startMode;
};
ScriptEngineType = 0x02,
CdbEngineType = 0x04,
PdbEngineType = 0x08,
+ TcfEngineType = 0x10,
AllEngineTypes = GdbEngineType
| ScriptEngineType
| CdbEngineType
| PdbEngineType
+ | TcfEngineType
};
QDebug operator<<(QDebug d, DebuggerState state);
friend class Internal::GdbEngine;
friend class Internal::ScriptEngine;
friend class Internal::PdbEngine;
+ friend class Internal::TcfEngine;
friend class Internal::CdbDebugEngine;
friend class Internal::CdbDebugEnginePrivate;
friend class Internal::TrkGdbAdapter;
void operateByInstructionTriggered();
void startFailed();
-private:
+public:
Internal::ModulesHandler *modulesHandler() const;
Internal::BreakHandler *breakHandler() const;
Internal::RegisterHandler *registerHandler() const;
Internal::ThreadsHandler *threadsHandler() const;
Internal::WatchHandler *watchHandler() const;
Internal::SnapshotHandler *snapshotHandler() const;
+
+private:
Internal::SourceFilesWindow *sourceFileWindow() const;
QWidget *threadsWindow() const;
public:
// stuff in this block should be made private by moving it to
// one of the interfaces
- QList<Internal::Symbol> moduleSymbols(const QString &moduleName);
+ void requestModuleSymbols(const QString &moduleName);
+ void showModuleSymbols(const QString &moduleName,
+ const QList<Internal::Symbol> &symbols);
signals:
void debuggingFinished();
void aboutToShutdown();
- void toggleBreakpoint(const QString &fileName, int lineNumber);
- Internal::BreakpointData *findBreakpoint(const QString &fileName, int lineNumber);
+ //void toggleBreakpoint(const QString &fileName, int lineNumber);
void setToolTipExpression(const QPoint &mousePos,
TextEditor::ITextEditor *editor, int cursorPos);
void openTextEditor(const QString &titlePattern,
const char * const STARTEXTERNAL = "Debugger.StartExternal";
const char * const ATTACHEXTERNAL = "Debugger.AttachExternal";
const char * const ATTACHCORE = "Debugger.AttachCore";
+const char * const ATTACHTCF = "Debugger.AttachTcf";
const char * const ATTACHREMOTE = "Debugger.AttachRemote";
const char * const DETACH = "Debugger.Detach";
}
return true;
}
- // engine disabling
+ // Engine disabling.
if (option == _("-disable-cdb")) {
*enabledEngines &= ~Debugger::CdbEngineType;
return true;
*enabledEngines &= ~Debugger::ScriptEngineType;
return true;
}
+ if (option == QLatin1String("-disable-tcf")) {
+ *enabledEngines &= ~TcfEngineType;
+ return true;
+ }
*errorMessage = DebuggerPlugin::tr("Invalid debugger option: %1").arg(option);
return false;
}
static bool parseArguments(const QStringList &args,
- DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters,
- unsigned *enabledEngines, QString *errorMessage)
+ DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters,
+ unsigned *enabledEngines, QString *errorMessage)
{
const QStringList::const_iterator cend = args.constEnd();
for (QStringList::const_iterator it = args.constBegin(); it != cend; ++it)
m_attachCoreAction->setText(tr("Attach to Core..."));
connect(m_attachCoreAction, SIGNAL(triggered()), this, SLOT(attachCore()));
+ m_attachTcfAction = new QAction(this);
+ m_attachTcfAction->setText(tr("Attach to Running Tcf Agent..."));
+ m_attachTcfAction->setToolTip(tr("This attaches to a running "
+ "'Target Communication Framework' agent."));
+ connect(m_attachTcfAction, SIGNAL(triggered()),
+ this, SLOT(attachRemoteTcf()));
+
m_startRemoteAction = new QAction(this);
m_startRemoteAction->setText(tr("Start and Attach to Remote Application..."));
connect(m_startRemoteAction, SIGNAL(triggered()),
cmd = am->registerAction(m_attachCoreAction,
Constants::ATTACHCORE, globalcontext);
+
cmd->setAttribute(Command::CA_Hide);
mstart->addAction(cmd, CC::G_DEFAULT_ONE);
+ cmd = am->registerAction(m_attachTcfAction,
+ Constants::ATTACHTCF, globalcontext);
+ mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
+
cmd = am->registerAction(m_startRemoteAction,
Constants::ATTACHREMOTE, globalcontext);
cmd->setAttribute(Command::CA_Hide);
if (!isDebuggable(editor))
return;
- QString fileName, position;
+ BreakHandler *handler = m_manager->breakHandler();
+ QTC_ASSERT(handler, return);
+
+ BreakpointData *data = 0;
+ QString position;
if (editor->property("DisassemblerView").toBool()) {
QString fileName = editor->file()->fileName();
QString line = editor->contents()
.section('\n', lineNumber - 1, lineNumber - 1);
- fileName = line.left(line.indexOf(QLatin1Char(' ')));
- lineNumber = -1;
position = _("*") + fileName;
+ BreakpointData needle;
+ needle.bpAddress = line.left(line.indexOf(QLatin1Char(' '))).toLatin1();
+ needle.bpLineNumber = "-1";
+ data = handler->findSimilarBreakpoint(needle);
} else {
- fileName = editor->file()->fileName();
+ QString fileName = editor->file()->fileName();
position = fileName + QString(":%1").arg(lineNumber);
+ BreakpointData needle;
+ needle.bpFileName = fileName;
+ needle.bpLineNumber = QByteArray::number(lineNumber);
+ data = handler->findSimilarBreakpoint(needle);
}
- BreakpointData *data = m_manager->findBreakpoint(fileName, lineNumber);
if (data) {
// existing breakpoint
{
QAction *act = qobject_cast<QAction *>(sender());
QTC_ASSERT(act, return);
+ BreakHandler *handler = m_manager->breakHandler();
+ QTC_ASSERT(handler, return);
QString str = act->data().toString();
int pos = str.lastIndexOf(':');
- m_manager->toggleBreakpoint(str.left(pos), str.mid(pos + 1).toInt());
+ toggleBreakpoint(str, pos);
}
void DebuggerPlugin::breakpointEnableDisableMarginActionTriggered()
QString str = act->data().toString();
int pos = str.lastIndexOf(':');
- QString fileName = str.left(pos);
- int lineNumber = str.mid(pos + 1).toInt();
-
- BreakpointData *data = handler->at(handler->findBreakpoint(fileName, lineNumber));
+ BreakpointData needle;
+ needle.bpFileName = str.left(pos);
+ needle.bpLineNumber = str.mid(pos + 1).toLatin1();
+ BreakpointData *data = handler->findSimilarBreakpoint(needle);
+ QTC_ASSERT(data, return);
handler->toggleBreakpointEnabled(data);
-
m_manager->attemptBreakpointSynchronization();
}
{
if (!isDebuggable(editor))
return;
- m_manager->toggleBreakpoint(editor->file()->fileName(), lineNumber);
+ toggleBreakpoint(editor->file()->fileName(), lineNumber);
}
void DebuggerPlugin::showToolTip(ITextEditor *editor, const QPoint &point, int pos)
configValue(_("LastExternalExecutableFile")).toString());
dlg.setExecutableArguments(
configValue(_("LastExternalExecutableArguments")).toString());
+ dlg.setWorkingDirectory(
+ configValue(_("LastExternalWorkingDirectory")).toString());
if (dlg.exec() != QDialog::Accepted)
return;
dlg.executableFile());
setConfigValue(_("LastExternalExecutableArguments"),
dlg.executableArguments());
+ setConfigValue(_("LastExternalWorkingDirectory"),
+ dlg.workingDirectory());
sp->executable = dlg.executableFile();
sp->startMode = StartExternal;
+ sp->workingDirectory = dlg.workingDirectory();
if (!dlg.executableArguments().isEmpty())
sp->processArgs = dlg.executableArguments().split(QLatin1Char(' '));
sp->debuggerCommand = dlg.debugger(); // Override toolchain-detection.
if (!sp->debuggerCommand.isEmpty())
sp->toolChainType = ProjectExplorer::ToolChain::INVALID;
- sp->startMode = StartRemote;
+ sp->startMode = AttachToRemote;
if (dlg.useServerStartScript())
sp->serverStartScript = dlg.serverStartScript();
sp->sysRoot = dlg.sysRoot();
m_manager->debuggerManagerActions().reverseDirectionAction->setChecked(false);
}
+static BreakpointData *createBreakpointByFileAndLine
+ (const QString &fileName, int lineNumber)
+{
+ BreakpointData *data = new BreakpointData;
+ if (lineNumber > 0) {
+ data->fileName = fileName;
+ data->lineNumber = QByteArray::number(lineNumber);
+ data->pending = true;
+ data->setMarkerFileName(fileName);
+ data->setMarkerLineNumber(lineNumber);
+ } else {
+ data->funcName = fileName;
+ data->lineNumber = 0;
+ data->pending = true;
+ // FIXME: Figure out in which disassembler view the Marker sits.
+ // Might be better to let the user code create the BreakpointData
+ // structure and insert it here.
+ data->setMarkerFileName(QString());
+ data->setMarkerLineNumber(0);
+ }
+ return data;
+}
+
void DebuggerPlugin::toggleBreakpoint()
{
ITextEditor *textEditor = currentTextEditor();
QTC_ASSERT(textEditor, return);
- QString fileName = textEditor->file()->fileName();
int lineNumber = textEditor->currentLine();
if (lineNumber >= 0)
- m_manager->toggleBreakpoint(fileName, lineNumber);
+ toggleBreakpoint(textEditor->file()->fileName(), lineNumber);
+}
+
+void DebuggerPlugin::toggleBreakpoint(const QString &fileName, int lineNumber)
+{
+ BreakHandler *handler = m_manager->breakHandler();
+ QTC_ASSERT(handler, return);
+ BreakpointData needle;
+ needle.bpFileName = fileName;
+ needle.bpLineNumber.setNum(lineNumber);
+ BreakpointData *data = handler->findSimilarBreakpoint(needle);
+ if (data) {
+ handler->removeBreakpoint(data);
+ } else {
+ data = new BreakpointData;
+ data->fileName = fileName;
+ data->lineNumber = QByteArray::number(lineNumber);
+ data->pending = true;
+ data->setMarkerFileName(fileName);
+ data->setMarkerLineNumber(lineNumber);
+ handler->appendBreakpoint(data);
+ }
+ m_manager->attemptBreakpointSynchronization();
+}
+
+void DebuggerPlugin::attachRemoteTcf()
+{
+ const DebuggerStartParametersPtr sp(new DebuggerStartParameters);
+ AttachTcfDialog dlg(m_uiSwitcher->mainWindow());
+ QStringList arches;
+ arches.append(_("i386:x86-64:intel"));
+ dlg.setRemoteArchitectures(arches);
+ dlg.setRemoteChannel(
+ configValue(_("LastTcfRemoteChannel")).toString());
+ dlg.setRemoteArchitecture(
+ configValue(_("LastTcfRemoteArchitecture")).toString());
+ dlg.setServerStartScript(
+ configValue(_("LastTcfServerStartScript")).toString());
+ dlg.setUseServerStartScript(
+ configValue(_("LastTcfUseServerStartScript")).toBool());
+ if (dlg.exec() != QDialog::Accepted)
+ return;
+ setConfigValue(_("LastTcfRemoteChannel"), dlg.remoteChannel());
+ setConfigValue(_("LastTcfRemoteArchitecture"), dlg.remoteArchitecture());
+ setConfigValue(_("LastTcfServerStartScript"), dlg.serverStartScript());
+ setConfigValue(_("LastTcfUseServerStartScript"), dlg.useServerStartScript());
+ sp->remoteChannel = dlg.remoteChannel();
+ sp->remoteArchitecture = dlg.remoteArchitecture();
+ sp->serverStartScript = dlg.serverStartScript();
+ sp->startMode = AttachTcf;
+ if (dlg.useServerStartScript())
+ sp->serverStartScript = dlg.serverStartScript();
+
+ if (RunControl *runControl = m_debuggerRunControlFactory->create(sp))
+ ProjectExplorerPlugin::instance()
+ ->startRunControl(runControl, PE::DEBUGMODE);
}
#include "debuggerplugin.moc"
~DebuggerPlugin();
private:
- virtual bool initialize(const QStringList &arguments, QString *error_message);
- virtual void aboutToShutdown();
- virtual void extensionsInitialized();
- virtual void remoteCommand(const QStringList &options, const QStringList &arguments);
+ bool initialize(const QStringList &arguments, QString *error_message);
+ void aboutToShutdown();
+ void extensionsInitialized();
+ void remoteCommand(const QStringList &options, const QStringList &arguments);
QVariant configValue(const QString &name) const;
TextEditor::ITextEditor *currentTextEditor();
private slots:
void activatePreviousMode();
void activateDebugMode();
- void editorOpened(Core::IEditor *);
- void editorAboutToClose(Core::IEditor *);
+ void editorOpened(Core::IEditor *editor);
+ void editorAboutToClose(Core::IEditor *editor);
void handleStateChanged(int state);
void requestMark(TextEditor::ITextEditor *editor, int lineNumber);
void showToolTip(TextEditor::ITextEditor *editor, const QPoint &pnt, int pos);
int lineNumber, QMenu *menu);
void resetLocation();
- void gotoLocation(const QString &file, int line, bool setMarker);
+ void gotoLocation(const QString &fileName, int lineNumber, bool setMarker);
void openTextEditor(const QString &titlePattern, const QString &contents);
void toggleBreakpoint();
+ void toggleBreakpoint(const QString &fileName, int lineNumber);
void breakpointSetRemoveMarginActionTriggered();
void breakpointEnableDisableMarginActionTriggered();
void onModeChanged(Core::IMode *mode);
void attachExternalApplication();
void attachCore();
void attachCmdLine();
+ void attachRemoteTcf();
void enableReverseDebuggingTriggered(const QVariant &value);
void languageChanged(const QString &debuggerLanguage);
QAction *m_startRemoteAction;
QAction *m_attachExternalAction;
QAction *m_attachCoreAction;
+ QAction *m_attachTcfAction;
QAction *m_detachAction;
QComboBox *m_langBox;
QToolButton *m_reverseToolButton;
DebuggerRunControl::DebuggerRunControl(DebuggerManager *manager,
LocalApplicationRunConfiguration *runConfiguration)
- : RunControl(runConfiguration),
+ : RunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE),
m_startParameters(new DebuggerStartParameters()),
m_manager(manager),
m_running(false)
m_startParameters->startMode = StartInternal;
m_startParameters->executable = runConfiguration->executable();
m_startParameters->environment = runConfiguration->environment().toStringList();
- m_startParameters->workingDir = runConfiguration->workingDirectory();
+ m_startParameters->workingDirectory = runConfiguration->workingDirectory();
m_startParameters->processArgs = runConfiguration->commandLineArguments();
switch (m_startParameters->toolChainType) {
break;
}
if (runConfiguration->target()->project()) {
- m_startParameters->buildDir =
+ m_startParameters->buildDirectory =
runConfiguration->target()->activeBuildConfiguration()->buildDirectory();
}
m_startParameters->useTerminal =
}
DebuggerRunControl::DebuggerRunControl(DebuggerManager *manager, const DebuggerStartParametersPtr &startParameters)
- : RunControl(0),
+ : RunControl(0, ProjectExplorer::Constants::DEBUGMODE),
m_startParameters(startParameters),
m_manager(manager),
m_running(false)
#include "abstractgdbadapter.h"
+#include "abstractgdbprocess.h"
+
#include <utils/qtcassert.h>
#include <QtCore/QProcess>
void AbstractGdbAdapter::write(const QByteArray &data)
{
- m_engine->m_gdbProc.write(data);
+ gdbProc()->write(data);
}
bool AbstractGdbAdapter::isTrkAdapter() const
namespace Debugger {
namespace Internal {
+class AbstractGdbProcess;
+
// AbstractGdbAdapter is inherited by PlainGdbAdapter used for local
// debugging and TrkGdbAdapter used for on-device debugging.
// In the PlainGdbAdapter case it's just a wrapper around a QProcess running
virtual void interruptInferior() = 0;
virtual void shutdown();
virtual const char *inferiorShutdownCommand() const;
+ virtual AbstractGdbProcess *gdbProc() = 0;
virtual DumperHandling dumperHandling() const = 0;
--- /dev/null
+/**************************************************************************
+**
+** 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 "abstractgdbprocess.h"
+
+namespace Debugger {
+namespace Internal {
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** 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 GDBPROCESSWRAPPER_H
+#define GDBPROCESSWRAPPER_H
+
+#include <QtCore/QObject>
+#include <QtCore/QProcess>
+
+namespace Debugger {
+namespace Internal {
+
+class AbstractGdbProcess : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(AbstractGdbProcess)
+public:
+ virtual QByteArray readAllStandardOutput() = 0;
+ virtual QByteArray readAllStandardError() = 0;
+
+ virtual void start(const QString &cmd, const QStringList &args) = 0;
+ virtual bool waitForStarted() = 0;
+ virtual qint64 write(const QByteArray &data) = 0;
+ virtual void kill() = 0;
+
+ virtual QProcess::ProcessState state() const = 0;
+ virtual QString errorString() const = 0;
+
+ virtual QProcessEnvironment processEnvironment() const = 0;
+ virtual void setProcessEnvironment(const QProcessEnvironment &env) = 0;
+ virtual void setEnvironment(const QStringList &env) = 0;
+ virtual void setWorkingDirectory(const QString &dir) = 0;
+
+ virtual ~AbstractGdbProcess() {}
+
+signals:
+ void error(QProcess::ProcessError);
+ void finished(int exitCode, QProcess::ExitStatus exitStatus);
+ void readyReadStandardError();
+ void readyReadStandardOutput();
+
+protected:
+ explicit AbstractGdbProcess(QObject *parent = 0) : QObject(parent) {}
+
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // GDBPROCESSWRAPPER_H
**
**************************************************************************/
-#include "plaingdbadapter.h"
+#include "abstractplaingdbadapter.h"
-#include "gdbengine.h"
-#include "procinterrupt.h"
-#include "debuggerstringutils.h"
#include "debuggeractions.h"
+#include "debuggerstringutils.h"
#include <utils/qtcassert.h>
-#include <QtCore/QFileInfo>
-
namespace Debugger {
namespace Internal {
#define CB(callback) \
- static_cast<GdbEngine::AdapterCallback>(&PlainGdbAdapter::callback), \
+ static_cast<GdbEngine::AdapterCallback>(&AbstractPlainGdbAdapter::callback), \
STRINGIFY(callback)
-///////////////////////////////////////////////////////////////////////
-//
-// PlainGdbAdapter
-//
-///////////////////////////////////////////////////////////////////////
-
-PlainGdbAdapter::PlainGdbAdapter(GdbEngine *engine, QObject *parent)
+AbstractPlainGdbAdapter::AbstractPlainGdbAdapter(GdbEngine *engine,
+ QObject *parent)
: AbstractGdbAdapter(engine, parent)
{
- // Output
- connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
- engine, SLOT(readDebugeeOutput(QByteArray)));
}
-AbstractGdbAdapter::DumperHandling PlainGdbAdapter::dumperHandling() const
-{
- // LD_PRELOAD fails for System-Qt on Mac.
-#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
- return DumperLoadedByGdb;
-#else
- return DumperLoadedByGdbPreload;
-#endif
-}
-
-void PlainGdbAdapter::startAdapter()
-{
- QTC_ASSERT(state() == EngineStarting, qDebug() << state());
- setState(AdapterStarting);
- debugMessage(_("TRYING TO START ADAPTER"));
-
- QStringList gdbArgs;
-
- if (!m_outputCollector.listen()) {
- emit adapterStartFailed(tr("Cannot set up communication with child process: %1")
- .arg(m_outputCollector.errorString()), QString());
- return;
- }
- gdbArgs.append(_("--tty=") + m_outputCollector.serverName());
-
- if (!startParameters().workingDir.isEmpty())
- m_engine->m_gdbProc.setWorkingDirectory(startParameters().workingDir);
- if (!startParameters().environment.isEmpty())
- m_engine->m_gdbProc.setEnvironment(startParameters().environment);
-
- if (!m_engine->startGdb(gdbArgs)) {
- m_outputCollector.shutdown();
- return;
- }
-
- emit adapterStarted();
-}
-void PlainGdbAdapter::startInferior()
+void AbstractPlainGdbAdapter::startInferior()
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
if (!startParameters().processArgs.isEmpty()) {
QString args = startParameters().processArgs.join(_(" "));
- m_engine->postCommand("-exec-arguments " + args.toLocal8Bit());
+ m_engine->postCommand("-exec-arguments " + toLocalEncoding(args));
}
- QFileInfo fi(startParameters().executable);
- QByteArray path = fi.absoluteFilePath().toLocal8Bit();
- m_engine->postCommand("-file-exec-and-symbols \"" + path + '"',
+ m_engine->postCommand("-file-exec-and-symbols \"" + execFilePath() + '"',
CB(handleFileExecAndSymbols));
}
-void PlainGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response)
+void AbstractPlainGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response)
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
if (response.resultClass == GdbResultDone) {
-#ifdef Q_OS_LINUX
- // Old gdbs do not announce the PID for programs without pthreads.
- // Note that successfully preloading the debugging helpers will
- // automatically load pthreads, so this will be unnecessary.
- if (m_engine->m_gdbVersion < 70000)
- m_engine->postCommand("info target", CB(handleInfoTarget));
-#endif
+ if (infoTargetNecessary()) {
+ // Old gdbs do not announce the PID for programs without pthreads.
+ // Note that successfully preloading the debugging helpers will
+ // automatically load pthreads, so this will be unnecessary.
+ if (m_engine->m_gdbVersion < 70000)
+ m_engine->postCommand("info target", CB(handleInfoTarget));
+ }
emit inferiorPrepared();
} else {
QByteArray ba = response.data.findChild("msg").data();
- QString msg = QString::fromLocal8Bit(ba);
+ QString msg = fromLocalEncoding(ba);
// Extend the message a bit in unknown cases.
if (!ba.endsWith("File format not recognized"))
msg = tr("Starting executable failed:\n") + msg;
}
}
-#ifdef Q_OS_LINUX
-void PlainGdbAdapter::handleInfoTarget(const GdbResponse &response)
-{
- if (response.resultClass == GdbResultDone) {
- // [some leading stdout here]
- // >&" Entry point: 0x80831f0 0x08048134 - 0x08048147 is .interp\n"
- // [some trailing stdout here]
- QString msg = _(response.data.findChild("consolestreamoutput").data());
- QRegExp needle(_("\\bEntry point: 0x([0-9a-f]+)\\b"));
- if (needle.indexIn(msg) != -1) {
- m_engine->m_entryPoint =
- "0x" + needle.cap(1).toLatin1().rightJustified(sizeof(void *) * 2, '0');
- m_engine->postCommand("tbreak *0x" + needle.cap(1).toAscii());
- // Do nothing here - inferiorPrepared handles the sequencing.
- } else {
- emit inferiorStartFailed(_("Parsing start address failed"));
- }
- } else if (response.resultClass == GdbResultError) {
- emit inferiorStartFailed(_("Fetching start address failed"));
- }
-}
-#endif
-
-void PlainGdbAdapter::startInferiorPhase2()
+void AbstractPlainGdbAdapter::startInferiorPhase2()
{
setState(InferiorRunningRequested);
m_engine->postCommand("-exec-run", GdbEngine::RunRequest, CB(handleExecRun));
}
-void PlainGdbAdapter::handleExecRun(const GdbResponse &response)
+void AbstractPlainGdbAdapter::handleExecRun(const GdbResponse &response)
{
if (response.resultClass == GdbResultRunning) {
QTC_ASSERT(state() == InferiorRunning, qDebug() << state());
m_engine->postCommand("target record");
} else {
QTC_ASSERT(state() == InferiorRunningRequested, qDebug() << state());
- QString msg = QString::fromLocal8Bit(response.data.findChild("msg").data());
+ QString msg = fromLocalEncoding(response.data.findChild("msg").data());
//QTC_ASSERT(status() == InferiorRunning, /**/);
//interruptInferior();
emit inferiorStartFailed(msg);
}
}
-void PlainGdbAdapter::interruptInferior()
+void AbstractPlainGdbAdapter::handleInfoTarget(const GdbResponse &response)
{
- const qint64 attachedPID = m_engine->inferiorPid();
- if (attachedPID <= 0) {
- debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
- return;
+ if (response.resultClass == GdbResultDone) {
+ // [some leading stdout here]
+ // >&" Entry point: 0x80831f0 0x08048134 - 0x08048147 is .interp\n"
+ // [some trailing stdout here]
+ QString msg = _(response.data.findChild("consolestreamoutput").data());
+ QRegExp needle(_("\\bEntry point: 0x([0-9a-f]+)\\b"));
+ if (needle.indexIn(msg) != -1) {
+ m_engine->m_entryPoint =
+ "0x" + needle.cap(1).toLatin1().rightJustified(sizeof(void *) * 2, '0');
+ m_engine->postCommand("tbreak *0x" + needle.cap(1).toAscii());
+ // Do nothing here - inferiorPrepared handles the sequencing.
+ } else {
+ emit inferiorStartFailed(_("Parsing start address failed"));
+ }
+ } else if (response.resultClass == GdbResultError) {
+ emit inferiorStartFailed(_("Fetching start address failed"));
}
-
- if (!interruptProcess(attachedPID))
- debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
}
-void PlainGdbAdapter::shutdown()
-{
- debugMessage(_("PLAIN ADAPTER SHUTDOWN %1").arg(state()));
- m_outputCollector.shutdown();
-}
-
-} // namespace Internal
} // namespace Debugger
+} // namespace Internal
--- /dev/null
+/**************************************************************************
+**
+** 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 ABSTRACTPLAINGDBADAPTER_H
+#define ABSTRACTPLAINGDBADAPTER_H
+
+#include "abstractgdbadapter.h"
+
+namespace Debugger {
+namespace Internal {
+
+class AbstractPlainGdbAdapter : public AbstractGdbAdapter
+{
+public:
+ AbstractPlainGdbAdapter(GdbEngine *engine, QObject *parent = 0);
+
+ virtual void startInferior();
+ virtual void startInferiorPhase2();
+
+protected:
+ void handleInfoTarget(const GdbResponse &response);
+
+private:
+ virtual QByteArray execFilePath() const = 0;
+ virtual bool infoTargetNecessary() const = 0;
+ virtual QByteArray toLocalEncoding(const QString &s) const = 0;
+ virtual QString fromLocalEncoding(const QByteArray &b) const = 0;
+ void handleExecRun(const GdbResponse &response);
+ void handleFileExecAndSymbols(const GdbResponse &response);
+
+};
+
+} // namespace Debugger
+} // namespace Internal
+
+#endif // ABSTRACTPLAINGDBADAPTER_H
#include "abstractgdbadapter.h"
+#include "abstractgdbprocess.h"
+
namespace Debugger {
namespace Internal {
void startInferior();
void interruptInferior();
const char *inferiorShutdownCommand() const { return "detach"; }
+ AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
private:
void handleAttach(const GdbResponse &response);
+
+ LocalGdbProcess m_gdbProc;
};
} // namespace Internal
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
+#include <QtGui/QMessageBox>
#if !defined(Q_OS_WIN)
#include <dlfcn.h>
m_debuggingHelperState = DebuggingHelperLoadTried;
QByteArray dlopenLib;
- if (startParameters().startMode == StartRemote)
- dlopenLib = startParameters().remoteDumperLib.toLocal8Bit();
+ if (startParameters().startMode == AttachToRemote
+ || startParameters().startMode == StartRemoteGdb)
+ dlopenLib = startParameters().remoteDumperLib;
else
dlopenLib = manager()->qtDumperLibraryName().toLocal8Bit();
QVariant::fromValue<StackCookie>(StackCookie(false, true)));
manager()->stackHandler()->setCurrentIndex(0);
if (supportsThreads())
- postCommand("-thread-list-ids", WatchUpdate, CB(handleStackListThreads), 0);
+ postCommand("-thread-list-ids", WatchUpdate, CB(handleThreadListIds), 0);
manager()->reloadRegisters();
updateLocals();
}
if (data.isValid())
list.push_back(data);
}
+
+ if (!m_resultVarName.isEmpty()) {
+ WatchData rd;
+ rd.iname = "return.0";
+ rd.name = "return";
+ rd.exp = m_resultVarName;
+ list.append(rd);
+ }
+
manager()->watchHandler()->insertBulkData(list);
manager()->watchHandler()->updateWatchers();
}
const QString successMsg = tr("Dumper version %1, %n custom dumpers found.",
0, m_dumperHelper.typeCount()).arg(dumperVersion);
showStatusMessage(successMsg);
+
+ // Sanity check for Qt version of dumpers and debuggee.
+ QByteArray ns = m_dumperHelper.qtNamespace().toLatin1();
+ postCommand("-var-create A@ * '" + ns + "qVersion'()",
+ CB(handleDebuggingHelperVersionCheckClassic));
+ postCommand("-var-delete A@");
} else {
// Retry if thread has not terminated yet.
m_debuggingHelperState = DebuggingHelperUnavailable;
//qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE";
}
+void GdbEngine::handleDebuggingHelperVersionCheckClassic(const GdbResponse &response)
+{
+ if (response.resultClass == GdbResultDone) {
+ QString value = _(response.data.findChild("value").data());
+ QString debuggeeQtVersion = value.section(QLatin1Char('"'), 1, 1);
+ QString dumperQtVersion = m_dumperHelper.qtVersionString();
+ if (dumperQtVersion != debuggeeQtVersion) {
+ manager()->showMessageBox(QMessageBox::Warning,
+ tr("Debugging helpers: Qt version mismatch"),
+ tr("The Qt version used to build the debugging helpers (%1) "
+ "does not match the Qt version used to build the debugged "
+ "application (%2).\nThis might yield incorrect results.")
+ .arg(dumperQtVersion).arg(debuggeeQtVersion));
+ } else {
+ debugMessage(_("DUMPER VERSION CHECK SUCCESSFUL: ")
+ + dumperQtVersion);
+ }
+ } else {
+ debugMessage("DUMPER VERSION CHECK NOT COMPLETED");
+ }
+}
+
void GdbEngine::handleVarListChildrenHelperClassic(const GdbMi &item,
const WatchData &parent)
{
#include "abstractgdbadapter.h"
+#include "abstractgdbprocess.h"
+
#ifdef Q_OS_LINUX
# define EXE_FROM_CORE
#endif
void startAdapter();
void startInferior();
void interruptInferior();
+ AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
private:
void loadExeAndSyms();
int m_round;
#endif
QString m_executable;
+ LocalGdbProcess m_gdbProc;
};
} // namespace Internal
$$PWD/abstractgdbadapter.h \
$$PWD/attachgdbadapter.h \
$$PWD/coregdbadapter.h \
- $$PWD/plaingdbadapter.h \
+ $$PWD/localplaingdbadapter.h \
$$PWD/termgdbadapter.h \
- $$PWD/remotegdbadapter.h \
+ $$PWD/remotegdbserveradapter.h \
$$PWD/trkgdbadapter.h \
- $$PWD/s60debuggerbluetoothstarter.h
+ $$PWD/s60debuggerbluetoothstarter.h \
+ $$PWD/abstractgdbprocess.h \
+ $$PWD/localgdbprocess.h \
+ $$PWD/remotegdbprocess.h \
+ $$PWD/remoteplaingdbadapter.h \
+ $$PWD/abstractplaingdbadapter.h
SOURCES += \
$$PWD/gdbmi.cpp \
$$PWD/abstractgdbadapter.cpp \
$$PWD/attachgdbadapter.cpp \
$$PWD/coregdbadapter.cpp \
- $$PWD/plaingdbadapter.cpp \
+ $$PWD/localplaingdbadapter.cpp \
$$PWD/termgdbadapter.cpp \
- $$PWD/remotegdbadapter.cpp \
+ $$PWD/remotegdbserveradapter.cpp \
$$PWD/trkgdbadapter.cpp \
- $$PWD/s60debuggerbluetoothstarter.cpp
+ $$PWD/s60debuggerbluetoothstarter.cpp \
+ $$PWD/abstractgdbprocess.cpp \
+ $$PWD/localgdbprocess.cpp \
+ $$PWD/remotegdbprocess.cpp \
+ $$PWD/remoteplaingdbadapter.cpp \
+ $$PWD/abstractplaingdbadapter.cpp
FORMS += $$PWD/gdboptionspage.ui
#include "attachgdbadapter.h"
#include "coregdbadapter.h"
-#include "plaingdbadapter.h"
+#include "localplaingdbadapter.h"
#include "termgdbadapter.h"
-#include "remotegdbadapter.h"
+#include "remotegdbserveradapter.h"
+#include "remoteplaingdbadapter.h"
#include "trkgdbadapter.h"
#include "watchutils.h"
#include "registerhandler.h"
#include "snapshothandler.h"
#include "stackhandler.h"
+#include "threadshandler.h"
#include "watchhandler.h"
#include "sourcefileswindow.h"
return DebuggerUISwitcher::instance()->mainWindow();
}
+AbstractGdbProcess *GdbEngine::gdbProc() const
+{
+ return m_gdbAdapter->gdbProc();
+}
+
GdbEngine::~GdbEngine()
{
// Prevent sending error messages afterwards.
- disconnect(&m_gdbProc, 0, this, 0);
+ if (m_gdbAdapter)
+ disconnect(gdbProc(), 0, this, 0);
delete m_gdbAdapter;
m_gdbAdapter = 0;
}
m_pendingLogStreamOutput.clear();
m_inbuffer.clear();
+ m_resultVarName.clear();
m_commandTimer->stop();
int progress = m_progress->progressValue();
m_progress->setProgressValue(qMin(70, progress + 1));
QByteArray id = result.findChild("id").data();
- showStatusMessage(tr("Thread group %1 created.").arg(_(id)), 1000);
+ showStatusMessage(tr("Thread group %1 created").arg(_(id)), 1000);
int pid = id.toInt();
if (pid != inferiorPid())
handleInferiorPidChanged(pid);
const GdbMi bkpt = result.findChild("bkpt");
const int number = bkpt.findChild("number").data().toInt();
BreakHandler *handler = manager()->breakHandler();
- BreakpointData *data = handler->findBreakpoint(number);
- breakpointDataFromOutput(data, bkpt);
+ BreakpointData *data = handler->findBreakpointByNumber(number);
+ setBreakpointDataFromOutput(data, bkpt);
handler->updateMarkers();
} else {
qDebug() << "IGNORED ASYNC OUTPUT"
void GdbEngine::readGdbStandardError()
{
- QByteArray err = m_gdbProc.readAllStandardError();
+ QByteArray err = gdbProc()->readAllStandardError();
debugMessage(_("UNEXPECTED GDB STDERR: " + err));
if (err == "Undefined command: \"bb\". Try \"help\".\n")
return;
int newstart = 0;
int scan = m_inbuffer.size();
- m_inbuffer.append(m_gdbProc.readAllStandardOutput());
+ m_inbuffer.append(gdbProc()->readAllStandardOutput());
// This can trigger when a dialog starts a nested event loop
if (m_busy)
} else if (state() == InferiorRunningRequested_Kill) {
debugMessage(_("RUNNING REQUESTED; POSTPONING INTERRUPT (KILL PENDING)"));
} else if (state() == InferiorRunning) {
- showStatusMessage(tr("Stopping temporarily."), 1000);
+ showStatusMessage(tr("Stopping temporarily"), 1000);
interruptInferiorTemporarily();
} else {
qDebug() << "ATTEMPTING TO QUEUE COMMAND IN INAPPROPRIATE STATE" << state();
void GdbEngine::flushQueuedCommands()
{
- showStatusMessage(tr("Processing queued commands."), 1000);
+ showStatusMessage(tr("Processing queued commands"), 1000);
while (!m_commandsToRunOnTemporaryBreak.isEmpty()) {
GdbCommand cmd = m_commandsToRunOnTemporaryBreak.takeFirst();
debugMessage(_("RUNNING QUEUED COMMAND " + cmd.command + ' '
debugMessage(_("KILLING DEBUGGER AS REQUESTED BY USER"));
// This is an undefined state, so we just pull the emergency brake.
manager()->watchHandler()->endCycle();
- m_gdbProc.kill();
+ gdbProc()->kill();
} else {
debugMessage(_("CONTINUE DEBUGGER AS REQUESTED BY USER"));
}
debugMessage(_("APPLYING WORKAROUND #1"));
showMessageBox(QMessageBox::Critical,
tr("Executable failed"), QString::fromLocal8Bit(msg));
- showStatusMessage(tr("Process failed to start."));
+ showStatusMessage(tr("Process failed to start"));
shutdown();
} else if (msg == "\"finish\" not meaningful in the outermost frame.") {
// Handle a case known to appear on gdb 6.4 symbianelf when
// ~"242\t x *= 2;"
//109^done"
setState(InferiorStopped);
- showStatusMessage(tr("Jumped. Stopped."));
+ showStatusMessage(tr("Jumped. Stopped"));
QByteArray output = response.data.findChild("logstreamoutput").data();
if (output.isEmpty())
return;
// // file="main.cpp",fullname="/tmp/g/main.cpp",line="37"}
// QTC_ASSERT(state() == InferiorStopping, qDebug() << state())
// setState(InferiorStopped);
-// showStatusMessage(tr("Function reached. Stopped."));
+// showStatusMessage(tr("Function reached. Stopped"));
// GdbMi frame = response.data.findChild("frame");
// StackFrame f = parseStackFrame(frame, 0);
// gotoLocation(f, true);
}
#endif
+ // FIXME: Replace the #ifdef by the "target" architecture
#ifdef Q_OS_LINUX
if (!m_entryPoint.isEmpty()) {
GdbMi frameData = data.findChild("frame");
}
#endif
- // seen on XP after removing a breakpoint while running
+ // This was seen on XP after removing a breakpoint while running
// >945*stopped,reason="signal-received",signal-name="SIGTRAP",
// signal-meaning="Trace/breakpoint trap",thread-id="2",
// frame={addr="0x7c91120f",func="ntdll!DbgUiConnectToDbg",
}
}
+ // Show return value if possible, usually with reason "function-finished".
+ // *stopped,reason="function-finished",frame={addr="0x080556da",
+ // func="testReturnValue",args=[],file="/../app.cpp",
+ // fullname="/../app.cpp",line="1611"},gdb-result-var="$1",
+ // return-value="{d = 0x808d998}",thread-id="1",stopped-threads="all",
+ // core="1"
+ GdbMi resultVar = data.findChild("gdb-result-var");
+ if (resultVar.isValid())
+ m_resultVarName = resultVar.data();
+ else
+ m_resultVarName.clear();
+
bool initHelpers = m_debuggingHelperState == DebuggingHelperUninitialized
|| m_debuggingHelperState == DebuggingHelperLoadTried;
// Don't load helpers on stops triggered by signals unless it's
&& reason == "signal-received") {
QByteArray name = data.findChild("signal-name").data();
if (name != STOP_SIGNAL
- && (startParameters().startMode != StartRemote
+ && (startParameters().startMode != AttachToRemote
|| name != CROSS_STOP_SIGNAL))
initHelpers = false;
}
reloadBreakListInternal();
}
- if (reason == "breakpoint-hit") {
+ if (reason == "watchpoint-trigger") {
+ // *stopped,reason="watchpoint-trigger",wpt={number="2",exp="*0xbfffed40"},
+ // value={old="1",new="0"},frame={addr="0x00451e1b",
+ // func="QScopedPointer",args=[{name="this",value="0xbfffed40"},
+ // {name="p",value="0x0"}],file="x.h",fullname="/home/.../x.h",line="95"},
+ // thread-id="1",stopped-threads="all",core="2"
+ GdbMi wpt = data.findChild("wpt");
+ QByteArray bpNumber = wpt.findChild("number").data();
+ QByteArray bpAddress = wpt.findChild("exp").data();
+ //QByteArray threadId = data.findChild("thread-id").data();
+ showStatusMessage(tr("Watchpoint %1 at %2 triggered:")
+ .arg(_(bpNumber), _(bpAddress)));
+ } else if (reason == "breakpoint-hit") {
QByteArray bpNumber = data.findChild("bkptno").data();
QByteArray threadId = data.findChild("thread-id").data();
- showStatusMessage(tr("Stopped at breakpoint %1 in thread %2.")
+ showStatusMessage(tr("Stopped at breakpoint %1 in thread %2")
.arg(_(bpNumber), _(threadId)));
} else {
QString reasontr = tr("Stopped: \"%1\"").arg(_(reason));
// Ignore these as they are showing up regularly when
// stopping debugging.
if (name != STOP_SIGNAL
- && (startParameters().startMode != StartRemote
+ && (startParameters().startMode != AttachToRemote
|| name != CROSS_STOP_SIGNAL)) {
QString msg = tr("<p>The inferior stopped because it received a "
"signal from the Operating System.<p>"
if (supportsThreads()) {
int currentId = data.findChild("thread-id").data().toInt();
- if (m_gdbAdapter->isTrkAdapter())
+ if (m_gdbAdapter->isTrkAdapter()) {
m_gdbAdapter->trkReloadThreads();
- else
- postCommand("-thread-list-ids", CB(handleStackListThreads), currentId);
+ } else if (m_isMacGdb) {
+ postCommand("-thread-list-ids", CB(handleThreadListIds), currentId);
+ } else {
+ // This is only available in gdb 7.1+.
+ postCommand("-thread-info", CB(handleThreadInfo), currentId);
+ }
}
//
manager()->reloadRegisters();
}
-#ifdef Q_OS_LINUX
void GdbEngine::handleInfoProc(const GdbResponse &response)
{
if (response.resultClass == GdbResultDone) {
maybeHandleInferiorPidChanged(re.cap(1));
}
}
-#endif
void GdbEngine::handleShowVersion(const GdbResponse &response)
{
QByteArray cmd = "set environment ";
cmd += Debugger::Constants::Internal::LD_PRELOAD_ENV_VAR;
cmd += ' ';
- cmd += manager()->qtDumperLibraryName().toLocal8Bit();
+ cmd += startParameters().startMode == StartRemoteGdb
+ ? startParameters().remoteDumperLib
+ : cmd += manager()->qtDumperLibraryName().toLocal8Bit();
postCommand(cmd);
m_debuggingHelperState = DebuggingHelperLoadTried;
}
m_gdbAdapter->shutdown();
// fall-through
case AdapterStartFailed: // Adapter "did something", but it did not help
- if (m_gdbProc.state() == QProcess::Running) {
+ if (gdbProc()->state() == QProcess::Running) {
m_commandsToRunOnTemporaryBreak.clear();
postCommand("-gdb-exit", GdbEngine::ExitRequest, CB(handleGdbExit));
} else {
// fall-through
case InferiorStopFailed: // Tough luck, I guess. But unreachable as of now anyway.
setState(EngineShuttingDown);
- m_gdbProc.kill();
+ gdbProc()->kill();
break;
}
}
QString msg = m_gdbAdapter->msgGdbStopFailed(
QString::fromLocal8Bit(response.data.findChild("msg").data()));
debugMessage(_("GDB WON'T EXIT (%1); KILLING IT").arg(msg));
- m_gdbProc.kill();
+ gdbProc()->kill();
}
}
{
disconnectDebuggingHelperActions();
shutdown();
- m_gdbProc.kill();
+ gdbProc()->kill();
}
int GdbEngine::currentFrame() const
switch (sp->startMode) {
case AttachCore:
return new CoreGdbAdapter(this);
- case StartRemote:
- return new RemoteGdbAdapter(this, sp->toolChainType);
+ case AttachToRemote:
+ return new RemoteGdbServerAdapter(this, sp->toolChainType);
+ case StartRemoteGdb:
+ return new RemotePlainGdbAdapter(this);
case AttachExternal:
return new AttachGdbAdapter(this);
default:
if (sp->useTerminal)
return new TermGdbAdapter(this);
- return new PlainGdbAdapter(this);
+ return new LocalPlainGdbAdapter(this);
}
}
| ReloadModuleSymbolsCapability | BreakOnThrowAndCatchCapability
| ReturnFromFunctionCapability
| CreateFullBacktraceCapability
+ | WatchpointCapability
| AddWatcherCapability;
}
//
//////////////////////////////////////////////////////////////////////
-void GdbEngine::breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
+void GdbEngine::setBreakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt)
{
if (!bkpt.isValid())
return;
if (ba.startsWith('<') && ba.endsWith('>'))
ba = ba.mid(1, ba.size() - 2);
data->bpFuncName = _(ba);
+ } else if (child.hasName("thread")) {
+ data->bpThreadSpec = child.data();
+ } else if (child.hasName("type")) {
+ if (child.data() == "breakpoint")
+ data->type = BreakpointData::BreakpointType;
+ else // FIXME: Incomplete list of cases.
+ data->type = BreakpointData::WatchpointType;
}
+ // This field is not present. Contents needs to be parsed from
+ // the plain "ignore" response.
+ //else if (child.hasName("ignore"))
+ // data->bpIgnoreCount = child.data();
}
- // This field is not present. Contents needs to be parsed from
- // the plain "ignore" response.
- //else if (child.hasName("ignore"))
- // data->bpIgnoreCount = child.data();
QString name;
if (!fullName.isEmpty()) {
return where;
}
-QByteArray GdbEngine::breakpointLocation(int index)
+QByteArray GdbEngine::breakpointLocation(const BreakpointData *data)
{
- const BreakpointData *data = manager()->breakHandler()->at(index);
if (!data->funcName.isEmpty())
return data->funcName.toLatin1();
// In this case, data->funcName is something like '*0xdeadbeef'
void GdbEngine::sendInsertBreakpoint(int index)
{
+ const BreakpointData *data = manager()->breakHandler()->at(index);
// Set up fallback in case of pending breakpoints which aren't handled
// by the MI interface.
+ if (data->type == BreakpointData::WatchpointType) {
+ postCommand("watch *" + data->address,
+ NeedsStop | RebuildBreakpointModel,
+ CB(handleWatchInsert), index);
+ return;
+ }
+
QByteArray cmd;
- if (m_isMacGdb)
+ if (m_isMacGdb) {
cmd = "-break-insert -l -1 -f ";
- else if (m_gdbAdapter->isTrkAdapter())
+ } else if (m_gdbAdapter->isTrkAdapter()) {
cmd = "-break-insert -h -f ";
- else if (m_gdbVersion >= 60800)
+ } else if (m_gdbVersion >= 70000) {
+ cmd = "-break-insert ";
+ if (!data->threadSpec.isEmpty())
+ cmd += "-p " + data->threadSpec;
+ cmd += " -f ";
+ } else if (m_gdbVersion >= 60800) {
// Probably some earlier version would work as well.
cmd = "-break-insert -f ";
- else
+ } else {
cmd = "-break-insert ";
+ }
//if (!data->condition.isEmpty())
// cmd += "-c " + data->condition + ' ';
- cmd += breakpointLocation(index);
+ cmd += breakpointLocation(data);
postCommand(cmd, NeedsStop | RebuildBreakpointModel,
CB(handleBreakInsert1), index);
}
+void GdbEngine::handleWatchInsert(const GdbResponse &response)
+{
+ int index = response.cookie.toInt();
+ if (response.resultClass == GdbResultDone) {
+ // "Hardware watchpoint 2: *0xbfffed40\n"
+ QByteArray ba = response.data.findChild("consolestreamoutput").data();
+ if (ba.startsWith("Hardware watchpoint ")) {
+ const int pos = ba.indexOf(':', 20);
+ BreakpointData *data = manager()->breakHandler()->at(index);
+ data->bpNumber = ba.mid(20, pos - 20);
+ manager()->breakHandler()->updateMarkers();
+ } else {
+ debugMessage(_("CANNOT PARSE WATCHPOINT FROM" + ba));
+ }
+ }
+}
+
void GdbEngine::handleBreakInsert1(const GdbResponse &response)
{
int index = response.cookie.toInt();
- BreakHandler *handler = manager()->breakHandler();
+ BreakpointData *data = manager()->breakHandler()->at(index);
if (response.resultClass == GdbResultDone) {
// Interesting only on Mac?
- BreakpointData *data = handler->at(index);
GdbMi bkpt = response.data.findChild("bkpt");
- breakpointDataFromOutput(data, bkpt);
+ setBreakpointDataFromOutput(data, bkpt);
} else {
// Some versions of gdb like "GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)"
// know how to do pending breakpoints using CLI but not MI. So try
// again with MI.
- QByteArray cmd = "break " + breakpointLocation(index);
+ QByteArray cmd = "break " + breakpointLocation(data);
postCommand(cmd, NeedsStop | RebuildBreakpointModel,
CB(handleBreakInsert2), index);
}
void GdbEngine::handleBreakList(const GdbMi &table)
{
- GdbMi body = table.findChild("body");
+ const GdbMi body = table.findChild("body");
QList<GdbMi> bkpts;
if (body.isValid()) {
// Non-Mac
}
BreakHandler *handler = manager()->breakHandler();
- for (int index = 0; index != bkpts.size(); ++index) {
- BreakpointData temp(handler);
- breakpointDataFromOutput(&temp, bkpts.at(index));
- int found = handler->findBreakpoint(temp);
- if (found != -1)
- breakpointDataFromOutput(handler->at(found), bkpts.at(index));
- //else
- //qDebug() << "CANNOT HANDLE RESPONSE" << bkpts.at(index).toString();
+ foreach (const GdbMi &bkpt, bkpts) {
+ BreakpointData temp;
+ setBreakpointDataFromOutput(&temp, bkpt);
+ BreakpointData *data = handler->findSimilarBreakpoint(temp);
+ //qDebug() << "\n\nGOT: " << bkpt.toString() << '\n' << temp.toString();
+ if (data) {
+ //qDebug() << " FROM: " << data->toString();
+ setBreakpointDataFromOutput(data, bkpt);
+ //qDebug() << " TO: " << data->toString();
+ } else {
+ //qDebug() << " NOTHING SUITABLE FOUND";
+ debugMessage(_("CANNOT FIND BP: " + bkpt.toString()));
+ }
}
m_breakListOutdated = false;
//
// gdb 6.3 does not produce any console output
BreakHandler *handler = manager()->breakHandler();
- BreakpointData *data = handler->findBreakpoint(bpNumber);
+ BreakpointData *data = handler->findBreakpointByNumber(bpNumber);
if (response.resultClass == GdbResultDone && data) {
QString msg = _(response.data.findChild("consolestreamoutput").data());
//if (msg.contains(__("Will stop next time breakpoint"))) {
{
int bpNumber = response.cookie.toInt();
BreakHandler *handler = manager()->breakHandler();
- BreakpointData *data = handler->findBreakpoint(bpNumber);
+ BreakpointData *data = handler->findBreakpointByNumber(bpNumber);
if (response.resultClass == GdbResultDone) {
// We just assume it was successful. Otherwise we had to parse
// the output stream data.
// constructor.
const int bpNumber = response.cookie.toInt();
const BreakHandler *handler = manager()->breakHandler();
- BreakpointData *data = handler->findBreakpoint(bpNumber);
+ BreakpointData *data = handler->findBreakpointByNumber(bpNumber);
if (data) {
QString str = QString::fromLocal8Bit(
response.data.findChild("consolestreamoutput").data());
// <_Z10testQStackv+142>.\n"
const int bpNumber = response.cookie.toInt();
const BreakHandler *handler = manager()->breakHandler();
- BreakpointData *data = handler->findBreakpoint(bpNumber);
+ BreakpointData *data = handler->findBreakpointByNumber(bpNumber);
if (!data)
return;
QByteArray ba = response.data.findChild("consolestreamoutput").data();
{
QTC_ASSERT(!m_sourcesListUpdating,
qDebug() << "SOURCES LIST CURRENTLY UPDATING"; return);
- debugMessage(tr("ATTEMPT BREAKPOINT SYNC"));
+ debugMessage(_("ATTEMPT BREAKPOINT SYNC"));
switch (state()) {
case InferiorStarting:
break;
default:
//qDebug() << "attempted breakpoint sync in state" << state();
+ debugMessage(_("... NOT POSSIBLE IN CURRENT STATE"));
return;
}
foreach (BreakpointData *data, handler->takeRemovedBreakpoints()) {
QByteArray bpNumber = data->bpNumber;
- debugMessage(_("DELETING BP " + bpNumber + " IN "
- + data->markerFileName().toLocal8Bit()));
- if (!bpNumber.trimmed().isEmpty())
+ if (!bpNumber.trimmed().isEmpty()) {
+ debugMessage(_("DELETING BP " + bpNumber + " IN "
+ + data->markerFileName().toLocal8Bit()));
postCommand("-break-delete " + bpNumber,
NeedsStop | RebuildBreakpointModel);
+ } else {
+ debugMessage(_("QUIETLY REMOVING UNNUMBERED BREAKPOINT"));
+ }
delete data;
}
else // Because gdb won't do both changes at a time anyway.
if (data->ignoreCount != data->bpIgnoreCount) {
// Update ignorecount if needed.
- postCommand("ignore " + data->bpNumber + ' ' + data->ignoreCount,
+ QByteArray ic = QByteArray::number(data->ignoreCount.toInt());
+ postCommand("ignore " + data->bpNumber + ' ' + ic,
NeedsStop | RebuildBreakpointModel,
CB(handleBreakIgnore), data->bpNumber.toInt());
continue;
if (!data->enabled && data->bpEnabled) {
postCommand("-break-disable " + data->bpNumber,
NeedsStop | RebuildBreakpointModel,
- CB(handleBreakInfo));
+ CB(handleBreakDisable), data->bpNumber.toInt());
data->bpEnabled = false;
continue;
}
+ if (data->threadSpec != data->bpThreadSpec && !data->bpNumber.isEmpty()) {
+ // The only way to change this seems to be to re-set the bp completely.
+ //qDebug() << "FIXME: THREAD: " << data->threadSpec << data->bpThreadSpec;
+ data->bpThreadSpec.clear();
+ postCommand("-break-delete " + data->bpNumber,
+ NeedsStop | RebuildBreakpointModel);
+ sendInsertBreakpoint(index);
+ continue;
+ }
if (data->bpAddress.startsWith("0x")
&& data->bpCorrectedLineNumber.isEmpty()) {
// Prevent endless loop.
reloadModulesInternal();
}
-QList<Symbol> GdbEngine::moduleSymbols(const QString &moduleName)
+void GdbEngine::requestModuleSymbols(const QString &moduleName)
{
QList<Symbol> rc;
bool success = false;
} while (false);
if (!success)
qWarning("moduleSymbols: %s\n", qPrintable(errorMessage));
- return rc;
+ manager()->showModuleSymbols(moduleName, rc);
}
void GdbEngine::reloadModules()
reloadRegisters();
}
-void GdbEngine::handleStackListThreads(const GdbResponse &response)
+void GdbEngine::handleThreadInfo(const GdbResponse &response)
+{
+ int id = response.cookie.toInt();
+ if (response.resultClass == GdbResultDone) {
+ // ^done,threads=[{id="1",target-id="Thread 0xb7fdc710 (LWP 4264)",
+ // frame={level="0",addr="0x080530bf",func="testQString",args=[],
+ // file="/.../app.cpp",fullname="/../app.cpp",line="1175"},
+ // state="stopped",core="0"}],current-thread-id="1"
+ const QList<GdbMi> items = response.data.findChild("threads").children();
+ QList<ThreadData> threads;
+ for (int index = 0, n = items.size(); index != n; ++index) {
+ bool ok = false;
+ const GdbMi item = items.at(index);
+ const GdbMi frame = item.findChild("frame");
+ ThreadData thread;
+ thread.id = item.findChild("id").data().toInt();
+ thread.targetId = item.findChild("target-id").data().toInt();
+ thread.core = QString::fromLatin1(item.findChild("core").data());
+ thread.state = QString::fromLatin1(item.findChild("state").data());
+ thread.address = frame.findChild("addr").data().toULongLong(&ok, 0);
+ thread.function = QString::fromLatin1(frame.findChild("func").data());
+ thread.fileName = QString::fromLatin1(frame.findChild("fullname").data());
+ thread.lineNumber = frame.findChild("line").data().toInt();
+ threads.append(thread);
+ }
+ ThreadsHandler *threadsHandler = manager()->threadsHandler();
+ threadsHandler->setThreads(threads);
+ int currentIndex = response.data.findChild("current-thread-id").data().toInt();
+ threadsHandler->setCurrentThread(currentIndex);
+ } else {
+ // Fall back for older versions: Try to get at least a list
+ // of running threads.
+ postCommand("-thread-list-ids", CB(handleThreadListIds), id);
+ }
+}
+
+void GdbEngine::handleThreadListIds(const GdbResponse &response)
{
int id = response.cookie.toInt();
// "72^done,{thread-ids={thread-id="2",thread-id="1"},number-of-threads="2"}
+ // In gdb 7.1+ additionally: current-thread-id="1"
const QList<GdbMi> items = response.data.findChild("thread-ids").children();
QList<ThreadData> threads;
int currentIndex = -1;
bool GdbEngine::startGdb(const QStringList &args, const QString &gdb, const QString &settingsIdHint)
{
- m_gdbProc.disconnect(); // From any previous runs
+ gdbProc()->disconnect(); // From any previous runs
m_gdb = QString::fromLatin1(qgetenv("QTC_DEBUGGER_PATH"));
if (m_gdb.isEmpty())
const QString winPythonVersion = QLatin1String(winPythonVersionC);
const QDir dir = fi.absoluteDir();
if (dir.exists(winPythonVersion)) {
- QProcessEnvironment environment = m_gdbProc.processEnvironment();
+ QProcessEnvironment environment = gdbProc()->processEnvironment();
const QString pythonPathVariable = QLatin1String("PYTHONPATH");
// Check for existing values.
if (environment.contains(pythonPathVariable)) {
environment.insert(pythonPathVariable, pythonPath);
manager()->showDebuggerOutput(LogMisc,
_("Python path: %1").arg(pythonPath));
- m_gdbProc.setProcessEnvironment(environment);
+ gdbProc()->setProcessEnvironment(environment);
}
foundPython = true;
}
}
if (!foundPython) {
debugMessage(_("UNSUPPORTED GDB %1 DOES NOT HAVE PYTHON.").arg(m_gdb));
- showStatusMessage(_("Gdb at %1 does not have python.").arg(m_gdb));
+ showStatusMessage(_("Gdb at %1 does not have python").arg(m_gdb));
}
#endif
- connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
+ connect(gdbProc(), SIGNAL(error(QProcess::ProcessError)),
SLOT(handleGdbError(QProcess::ProcessError)));
- connect(&m_gdbProc, SIGNAL(finished(int, QProcess::ExitStatus)),
+ connect(gdbProc(), SIGNAL(finished(int, QProcess::ExitStatus)),
SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
- connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
+ connect(gdbProc(), SIGNAL(readyReadStandardOutput()),
SLOT(readGdbStandardOutput()));
- connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
+ connect(gdbProc(), SIGNAL(readyReadStandardError()),
SLOT(readGdbStandardError()));
- m_gdbProc.start(m_gdb, gdbArgs);
+ gdbProc()->start(m_gdb, gdbArgs);
- if (!m_gdbProc.waitForStarted()) {
+ if (!gdbProc()->waitForStarted()) {
const QString msg = tr("Unable to start gdb '%1': %2")
- .arg(m_gdb, m_gdbProc.errorString());
+ .arg(m_gdb, gdbProc()->errorString());
handleAdapterStartFailed(msg, settingsIdHint);
return false;
}
case QProcess::WriteError:
case QProcess::Timedout:
default:
- m_gdbProc.kill();
+ gdbProc()->kill();
setState(EngineShuttingDown, true);
showMessageBox(QMessageBox::Critical, tr("Gdb I/O Error"),
errorMessage(error));
setState(AdapterStartFailed, true);
// No point in being friendly here ...
- m_gdbProc.kill();
+ gdbProc()->kill();
if (!msg.isEmpty())
showMessageBox(QMessageBox::Critical, tr("Adapter crashed"), msg);
#include "idebuggerengine.h"
#include "debuggermanager.h" // only for StartParameters
#include "gdbmi.h"
+#include "localgdbprocess.h"
#include "watchutils.h"
#include <QtCore/QByteArray>
#include <QtCore/QMap>
#include <QtCore/QMultiMap>
#include <QtCore/QObject>
-#include <QtCore/QProcess>
#include <QtCore/QPoint>
#include <QtCore/QSet>
#include <QtCore/QTextCodec>
namespace Internal {
class AbstractGdbAdapter;
+class AbstractGdbProcess;
class GdbResponse;
class GdbMi;
class AttachGdbAdapter;
class CoreGdbAdapter;
-class PlainGdbAdapter;
-class RemoteGdbAdapter;
+class LocalPlainGdbAdapter;
+class RemoteGdbServerAdapter;
class TrkGdbAdapter;
enum DebuggingHelperState
private:
friend class AbstractGdbAdapter;
+ friend class AbstractPlainGdbAdapter;
friend class AttachGdbAdapter;
friend class CoreGdbAdapter;
- friend class PlainGdbAdapter;
+ friend class LocalPlainGdbAdapter;
friend class TermGdbAdapter;
- friend class RemoteGdbAdapter;
+ friend class RemoteGdbServerAdapter;
+ friend class RemotePlainGdbAdapter;
friend class TrkGdbAdapter;
private: ////////// General Interface //////////
QByteArray m_inbuffer;
bool m_busy;
- QProcess m_gdbProc;
AbstractGdbAdapter *m_gdbAdapter;
+ // Name of the convenience variable containing the last
+ // known function return value.
+ QByteArray m_resultVarName;
+
private: ////////// Gdb Command Management //////////
public: // Otherwise the Qt flag macros are unhappy.
qint64 inferiorPid() const { return m_manager->inferiorPid(); }
void handleInferiorPidChanged(qint64 pid) { manager()->notifyInferiorPidChanged(pid); }
void maybeHandleInferiorPidChanged(const QString &pid);
-
-#ifdef Q_OS_LINUX
void handleInfoProc(const GdbResponse &response);
QByteArray m_entryPoint;
-#endif
QFutureInterface<void> *m_progress;
private: ////////// View & Data Stuff //////////
void handleBreakInsert2(const GdbResponse &response);
void handleBreakCondition(const GdbResponse &response);
void handleBreakInfo(const GdbResponse &response);
+ void handleWatchInsert(const GdbResponse &response);
void handleInfoLine(const GdbResponse &response);
void extractDataFromInfoBreak(const QString &output, BreakpointData *data);
- void breakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt);
- QByteArray breakpointLocation(int index);
+ void setBreakpointDataFromOutput(BreakpointData *data, const GdbMi &bkpt);
+ QByteArray breakpointLocation(const BreakpointData *data);
void sendInsertBreakpoint(int index);
QString breakLocation(const QString &file) const;
void reloadBreakListInternal();
//
virtual void loadSymbols(const QString &moduleName);
virtual void loadAllSymbols();
- virtual QList<Symbol> moduleSymbols(const QString &moduleName);
+ virtual void requestModuleSymbols(const QString &moduleName);
virtual void reloadModules();
void reloadModulesInternal();
void handleModulesList(const GdbResponse &response);
void handleStackListFrames(const GdbResponse &response);
void handleStackSelectThread(const GdbResponse &response);
void handleStackSelectFrame(const GdbResponse &response);
- void handleStackListThreads(const GdbResponse &response);
+ void handleThreadListIds(const GdbResponse &response);
+ void handleThreadInfo(const GdbResponse &response);
Q_SLOT void reloadStack(bool forceGotoLocation);
Q_SLOT virtual void reloadFullStack();
int currentFrame() const;
void handleDebuggingHelperValue3Classic(const GdbResponse &response);
void handleDebuggingHelperEditValue(const GdbResponse &response);
void handleDebuggingHelperSetup(const GdbResponse &response);
+ void handleDebuggingHelperVersionCheckClassic(const GdbResponse &response);
Q_SLOT void createFullBacktrace();
void handleCreateFullBacktrace(const GdbResponse &response);
int buttons = 0);
void debugMessage(const QString &msg);
QMainWindow *mainWindow() const;
+ AbstractGdbProcess *gdbProc() const;
static QString m_toolTipExpression;
static QPoint m_toolTipPos;
--- /dev/null
+/**************************************************************************
+**
+** 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 "localgdbprocess.h"
+
+namespace Debugger {
+namespace Internal {
+
+LocalGdbProcess::LocalGdbProcess(QObject *parent) : AbstractGdbProcess(parent)
+{
+ connect(&m_gdbProc, SIGNAL(error(QProcess::ProcessError)),
+ this, SIGNAL(error(QProcess::ProcessError)));
+ connect(&m_gdbProc, SIGNAL(finished(int,QProcess::ExitStatus)),
+ this, SIGNAL(finished(int,QProcess::ExitStatus)));
+ connect(&m_gdbProc, SIGNAL(readyReadStandardOutput()),
+ this, SIGNAL(readyReadStandardOutput()));
+ connect(&m_gdbProc, SIGNAL(readyReadStandardError()),
+ this, SIGNAL(readyReadStandardError()));
+}
+
+QByteArray LocalGdbProcess::readAllStandardOutput()
+{
+ return m_gdbProc.readAllStandardOutput();
+}
+
+QByteArray LocalGdbProcess::readAllStandardError()
+{
+ return m_gdbProc.readAllStandardError();
+}
+
+void LocalGdbProcess::start(const QString &cmd, const QStringList &args)
+{
+ m_gdbProc.start(cmd, args);
+}
+
+bool LocalGdbProcess::waitForStarted()
+{
+ return m_gdbProc.waitForStarted();
+}
+
+qint64 LocalGdbProcess::write(const QByteArray &data)
+{
+ return m_gdbProc.write(data);
+}
+
+void LocalGdbProcess::kill()
+{
+ m_gdbProc.kill();
+}
+
+QProcess::ProcessState LocalGdbProcess::state() const
+{
+ return m_gdbProc.state();
+}
+
+QString LocalGdbProcess::errorString() const
+{
+ return m_gdbProc.errorString();
+}
+
+QProcessEnvironment LocalGdbProcess::processEnvironment() const
+{
+ return m_gdbProc.processEnvironment();
+}
+
+void LocalGdbProcess::setProcessEnvironment(const QProcessEnvironment &env)
+{
+ m_gdbProc.setProcessEnvironment(env);
+}
+
+void LocalGdbProcess::setEnvironment(const QStringList &env)
+{
+ m_gdbProc.setEnvironment(env);
+}
+
+void LocalGdbProcess::setWorkingDirectory(const QString &dir)
+{
+ m_gdbProc.setWorkingDirectory(dir);
+}
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** 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 LOCALGDBPROCESS_H
+#define LOCALGDBPROCESS_H
+
+#include "abstractgdbprocess.h"
+
+namespace Debugger {
+namespace Internal {
+
+class LocalGdbProcess : public AbstractGdbProcess
+{
+public:
+ explicit LocalGdbProcess(QObject *parent = 0);
+
+ virtual QByteArray readAllStandardOutput();
+ virtual QByteArray readAllStandardError();
+
+ virtual void start(const QString &cmd, const QStringList &args);
+ virtual bool waitForStarted();
+ virtual qint64 write(const QByteArray &data);
+ virtual void kill();
+
+ virtual QProcess::ProcessState state() const;
+ virtual QString errorString() const;
+
+ virtual QProcessEnvironment processEnvironment() const;
+ virtual void setProcessEnvironment(const QProcessEnvironment &env);
+ virtual void setEnvironment(const QStringList &env);
+ virtual void setWorkingDirectory(const QString &dir);
+
+private:
+ QProcess m_gdbProc;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // LOCALGDBPROCESS_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "localplaingdbadapter.h"
+
+#include "gdbengine.h"
+#include "procinterrupt.h"
+#include "debuggerstringutils.h"
+
+#include <utils/qtcassert.h>
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QProcess>
+#include <QtGui/QMessageBox>
+
+namespace Debugger {
+namespace Internal {
+
+///////////////////////////////////////////////////////////////////////
+//
+// PlainGdbAdapter
+//
+///////////////////////////////////////////////////////////////////////
+
+LocalPlainGdbAdapter::LocalPlainGdbAdapter(GdbEngine *engine, QObject *parent)
+ : AbstractPlainGdbAdapter(engine, parent)
+{
+ // Output
+ connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
+ engine, SLOT(readDebugeeOutput(QByteArray)));
+}
+
+AbstractGdbAdapter::DumperHandling LocalPlainGdbAdapter::dumperHandling() const
+{
+ // LD_PRELOAD fails for System-Qt on Mac.
+#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
+ return DumperLoadedByGdb;
+#else
+ return DumperLoadedByGdbPreload;
+#endif
+}
+
+void LocalPlainGdbAdapter::startAdapter()
+{
+ QTC_ASSERT(state() == EngineStarting, qDebug() << state());
+ setState(AdapterStarting);
+ debugMessage(_("TRYING TO START ADAPTER"));
+
+ QStringList gdbArgs;
+
+ if (!m_outputCollector.listen()) {
+ emit adapterStartFailed(tr("Cannot set up communication with child process: %1")
+ .arg(m_outputCollector.errorString()), QString());
+ return;
+ }
+ gdbArgs.append(_("--tty=") + m_outputCollector.serverName());
+
+ if (!startParameters().workingDirectory.isEmpty())
+ m_gdbProc.setWorkingDirectory(startParameters().workingDirectory);
+ if (!startParameters().environment.isEmpty())
+ m_gdbProc.setEnvironment(startParameters().environment);
+
+ if (!m_engine->startGdb(gdbArgs)) {
+ m_outputCollector.shutdown();
+ return;
+ }
+
+ emit adapterStarted();
+ checkForReleaseBuild();
+}
+
+void LocalPlainGdbAdapter::checkForReleaseBuild()
+{
+ // Quick check for a "release" build
+ QProcess proc;
+ QStringList args;
+ args.append(_("-h"));
+ args.append(_("-j"));
+ args.append(_(".debug_info"));
+ args.append(startParameters().executable);
+ proc.start(_("objdump"), args);
+ proc.closeWriteChannel();
+ QTC_ASSERT(proc.waitForStarted(), qDebug() << "UNABLE TO RUN OBJDUMP");
+ proc.waitForFinished();
+ QByteArray ba = proc.readAllStandardOutput();
+ // This should yield something like
+ // "debuggertest: file format elf32-i386\n\n"
+ // "Sections:\nIdx Name Size VMA LMA File off Algn\n"
+ // "30 .debug_info 00087d36 00000000 00000000 0006bbd5 2**0\n"
+ // " CONTENTS, READONLY, DEBUGGING"
+ if (ba.contains("Sections:") && !ba.contains(".debug_info")) {
+ m_engine->showMessageBox(QMessageBox::Information, "Warning",
+ tr("This does not seem to be a \"Debug\" build.\n"
+ "Setting breakpoints by file name and line number may fail."));
+ }
+}
+
+void LocalPlainGdbAdapter::interruptInferior()
+{
+ const qint64 attachedPID = m_engine->inferiorPid();
+ if (attachedPID <= 0) {
+ debugMessage(_("TRYING TO INTERRUPT INFERIOR BEFORE PID WAS OBTAINED"));
+ return;
+ }
+
+ if (!interruptProcess(attachedPID))
+ debugMessage(_("CANNOT INTERRUPT %1").arg(attachedPID));
+}
+
+void LocalPlainGdbAdapter::shutdown()
+{
+ debugMessage(_("PLAIN ADAPTER SHUTDOWN %1").arg(state()));
+ m_outputCollector.shutdown();
+}
+
+QByteArray LocalPlainGdbAdapter::execFilePath() const
+{
+ return QFileInfo(startParameters().executable)
+ .absoluteFilePath().toLocal8Bit();
+}
+
+bool LocalPlainGdbAdapter::infoTargetNecessary() const
+{
+#ifdef Q_OS_LINUX
+ return true;
+#else
+ return false;
+#endif
+}
+
+QByteArray LocalPlainGdbAdapter::toLocalEncoding(const QString &s) const
+{
+ return s.toLocal8Bit();
+}
+
+QString LocalPlainGdbAdapter::fromLocalEncoding(const QByteArray &b) const
+{
+ return QString::fromLocal8Bit(b);
+}
+
+} // namespace Internal
+} // namespace Debugger
#ifndef DEBUGGER_PLAINGDBADAPTER_H
#define DEBUGGER_PLAINGDBADAPTER_H
-#include "abstractgdbadapter.h"
+#include "abstractplaingdbadapter.h"
+
+#include "abstractgdbprocess.h"
#include <outputcollector.h>
//
///////////////////////////////////////////////////////////////////////
-class PlainGdbAdapter : public AbstractGdbAdapter
+class LocalPlainGdbAdapter : public AbstractPlainGdbAdapter
{
Q_OBJECT
public:
- PlainGdbAdapter(GdbEngine *engine, QObject *parent = 0);
+ LocalPlainGdbAdapter(GdbEngine *engine, QObject *parent = 0);
virtual DumperHandling dumperHandling() const;
void startAdapter();
- void startInferior();
- void startInferiorPhase2();
void interruptInferior();
void shutdown();
- const char *inferiorShutdownCommand() const { return "kill"; }
+ AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
private:
- void handleFileExecAndSymbols(const GdbResponse &response);
- void handleExecRun(const GdbResponse &response);
-#ifdef Q_OS_LINUX
- void handleInfoTarget(const GdbResponse &response);
-#endif
+ virtual QByteArray execFilePath() const;
+ virtual bool infoTargetNecessary() const;
+ virtual QByteArray toLocalEncoding(const QString &s) const;
+ virtual QString fromLocalEncoding(const QByteArray &b) const;
+ void checkForReleaseBuild();
OutputCollector m_outputCollector;
+ LocalGdbProcess m_gdbProc;
};
} // namespace Internal
options += "defaults,";
options.chop(1);
+ QByteArray resultVar;
+ if (!m_resultVarName.isEmpty())
+ resultVar = "resultvarname:" + m_resultVarName + ' ';
+
postCommand("bb options:" + options + " vars:" + varList + ' '
- + expanded + " watchers:" + watchers.toHex(),
+ + resultVar + expanded + " watchers:" + watchers.toHex(),
WatchUpdate, CB(handleStackFramePython));
}
//qDebug() << "SECOND CHUNK: " << out;
int pos = out.indexOf("data=");
if (pos != 0) {
- qDebug() << "DISCARDING JUNK AT BEGIN OF RESPONSE: "
- << out.left(pos);
+ debugMessage(_("DISCARDING JUNK AT BEGIN OF RESPONSE: "
+ + out.left(pos)));
out = out.mid(pos);
}
GdbMi all;
if (m_gdbAdapter->isTrkAdapter())
m_gdbAdapter->trkReloadThreads();
else
- postCommand("-thread-list-ids", CB(handleStackListThreads), 0);
+ postCommand("-thread-list-ids", CB(handleThreadListIds), 0);
manager()->reloadRegisters();
updateLocals();
}
--- /dev/null
+/**************************************************************************
+**
+** 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 "remotegdbprocess.h"
+
+#include "remoteplaingdbadapter.h"
+
+namespace Debugger {
+namespace Internal {
+
+RemoteGdbProcess::RemoteGdbProcess(const Core::SshServerInfo &server,
+ RemotePlainGdbAdapter *adapter, QObject *parent)
+ : AbstractGdbProcess(parent), m_serverInfo(server), m_adapter(adapter)
+{
+
+}
+
+QByteArray RemoteGdbProcess::readAllStandardOutput()
+{
+ QByteArray output = m_gdbOutput;
+ m_gdbOutput.clear();
+ return output;
+}
+
+QByteArray RemoteGdbProcess::readAllStandardError()
+{
+ QByteArray errorOutput = m_errorOutput;
+ m_errorOutput.clear();
+ return errorOutput;
+}
+
+void RemoteGdbProcess::start(const QString &cmd, const QStringList &args)
+{
+ m_gdbConn = Core::InteractiveSshConnection::create(m_serverInfo);
+ m_appOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
+ m_errOutputConn = Core::InteractiveSshConnection::create(m_serverInfo);
+ m_command = cmd;
+ m_cmdArgs = args;
+ m_errOutputConn->start();
+ m_appOutputConn->start();
+ m_gdbConn->start();
+ }
+
+bool RemoteGdbProcess::waitForStarted()
+{
+ if (!waitForInputReady(m_appOutputConn))
+ return false;
+ if (!sendAndWaitForEcho(m_appOutputConn, readerCmdLine(AppOutputFile)))
+ return false;
+ if (!waitForInputReady(m_errOutputConn))
+ return false;
+ if (!sendAndWaitForEcho(m_errOutputConn, readerCmdLine(ErrOutputFile)))
+ return false;
+ if (!waitForInputReady(m_gdbConn))
+ return false;
+ connect(m_appOutputConn.data(), SIGNAL(remoteOutputAvailable()),
+ this, SLOT(handleAppOutput()));
+ connect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()),
+ this, SLOT(handleErrOutput()));
+ connect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
+ this, SLOT(handleGdbOutput()));
+ m_gdbStarted = false;
+ m_gdbCmdLine = "stty -echo && DISPLAY=:0.0 " + m_command.toUtf8() + ' '
+ + m_cmdArgs.join(QLatin1String(" ")).toUtf8()
+ + " -tty=" + AppOutputFile + " 2>" + ErrOutputFile + '\n';
+ if (!m_wd.isEmpty())
+ m_gdbCmdLine.prepend("cd " + m_wd.toUtf8() + " && ");
+ if (sendInput(m_gdbCmdLine) != m_gdbCmdLine.count())
+ return false;
+
+ return true;
+}
+
+qint64 RemoteGdbProcess::write(const QByteArray &data)
+{
+ if (!m_gdbStarted || !m_inputToSend.isEmpty() || !m_lastSeqNr.isEmpty()) {
+ m_inputToSend.enqueue(data);
+ return data.size();
+ } else {
+ return sendInput(data);
+ }
+}
+
+void RemoteGdbProcess::kill()
+{
+ stopReaders();
+ Core::InteractiveSshConnection::Ptr controlConn
+ = Core::InteractiveSshConnection::create(m_serverInfo);
+ if (!controlConn->hasError()) {
+ if (controlConn->start())
+ controlConn->sendInput("pkill -x gdb\r\n");
+ }
+
+ m_gdbConn->quit();
+ emit finished(0, QProcess::CrashExit);
+}
+
+QProcess::ProcessState RemoteGdbProcess::state() const
+{
+ return m_gdbStarted ? QProcess::Running : QProcess::Starting;
+}
+
+QString RemoteGdbProcess::errorString() const
+{
+ return m_gdbConn ? m_gdbConn->error() : QString();
+}
+
+void RemoteGdbProcess::handleGdbOutput()
+{
+ m_currentGdbOutput
+ += removeCarriageReturn(m_gdbConn->waitForRemoteOutput(0));
+#if 0
+ qDebug("%s: complete unread output is '%s'", Q_FUNC_INFO, m_currentGdbOutput.data());
+#endif
+ if (checkForGdbExit(m_currentGdbOutput)) {
+ m_currentGdbOutput.clear();
+ return;
+ }
+
+ if (!m_currentGdbOutput.endsWith('\n'))
+ return;
+
+ if (!m_gdbStarted) {
+ const int index = m_currentGdbOutput.indexOf(m_gdbCmdLine);
+ if (index != -1)
+ m_currentGdbOutput.remove(index, m_gdbCmdLine.size());
+ // Note: We can't guarantee that we will match the command line,
+ // because the remote terminal sometimes inserts control characters.
+ // Otherwise we could set m_gdbStarted here.
+ }
+
+ m_gdbStarted = true;
+
+ if (m_currentGdbOutput.contains(m_lastSeqNr + '^'))
+ m_lastSeqNr.clear();
+
+ if (m_lastSeqNr.isEmpty() && !m_inputToSend.isEmpty()) {
+#if 0
+ qDebug("Sending queued command: %s", m_inputToSend.head().data());
+#endif
+ sendInput(m_inputToSend.dequeue());
+ }
+
+ if (!m_currentGdbOutput.isEmpty()) {
+ const int startPos
+ = m_gdbOutput.isEmpty() ? findAnchor(m_currentGdbOutput) : 0;
+ if (startPos != -1) {
+ m_gdbOutput += m_currentGdbOutput.mid(startPos);
+ m_currentGdbOutput.clear();
+ emit readyReadStandardOutput();
+ }
+ }
+}
+
+QProcessEnvironment RemoteGdbProcess::processEnvironment() const
+{
+ return QProcessEnvironment(); // TODO: Provide actual environment.
+}
+
+void RemoteGdbProcess::setProcessEnvironment(const QProcessEnvironment & /* env */)
+{
+ // TODO: Do something.
+}
+
+void RemoteGdbProcess::setEnvironment(const QStringList & /* env */)
+{
+ // TODO: Do something.
+}
+
+void RemoteGdbProcess::setWorkingDirectory(const QString &dir)
+{
+ m_wd = dir;
+}
+
+int RemoteGdbProcess::findAnchor(const QByteArray &data) const
+{
+ for (int pos = 0; pos < data.count(); ++pos) {
+ const char c = data.at(pos);
+ if (isdigit(c) || c == '*' || c == '+' || c == '=' || c == '~'
+ || c == '@' || c == '&' || c == '^')
+ return pos;
+ }
+ return -1;
+}
+
+qint64 RemoteGdbProcess::sendInput(const QByteArray &data)
+{
+ int pos;
+ for (pos = 0; pos < data.size(); ++pos)
+ if (!isdigit(data.at(pos)))
+ break;
+ m_lastSeqNr = data.left(pos);
+ return m_gdbConn->sendInput(data) ? data.size() : 0;
+}
+
+void RemoteGdbProcess::handleAppOutput()
+{
+ m_adapter->handleApplicationOutput(m_appOutputConn->waitForRemoteOutput(0));
+}
+
+void RemoteGdbProcess::handleErrOutput()
+{
+ m_errorOutput += m_errOutputConn->waitForRemoteOutput(0);
+ emit readyReadStandardError();
+}
+
+void RemoteGdbProcess::stopReaders()
+{
+ if (m_appOutputConn) {
+ disconnect(m_appOutputConn.data(), SIGNAL(remoteOutputAvailable()),
+ this, SLOT(handleAppOutput()));
+ m_appOutputConn->sendInput(CtrlC);
+ m_appOutputConn->quit();
+ }
+ if (m_errOutputConn) {
+ disconnect(m_errOutputConn.data(), SIGNAL(remoteOutputAvailable()),
+ this, SLOT(handleErrOutput()));
+ m_errOutputConn->sendInput(CtrlC);
+ m_errOutputConn->quit();
+ }
+}
+
+QByteArray RemoteGdbProcess::readerCmdLine(const QByteArray &file)
+{
+ return "rm -f " + file + " && mkfifo " + file + " && cat " + file + "\r\n";
+}
+
+QByteArray RemoteGdbProcess::removeCarriageReturn(const QByteArray &data)
+{
+ QByteArray output;
+ for (int i = 0; i < data.size(); ++i) {
+ const char c = data.at(i);
+ if (c != '\r')
+ output += c;
+ }
+ return output;
+}
+
+bool RemoteGdbProcess::checkForGdbExit(QByteArray &output)
+{
+ const QByteArray exitString("^exit");
+ const int exitPos = output.indexOf(exitString);
+ if (exitPos == -1)
+ return false;
+
+ emit finished(0, QProcess::NormalExit);
+ disconnect(m_gdbConn.data(), SIGNAL(remoteOutputAvailable()),
+ this, SLOT(handleGdbOutput()));
+ output.remove(exitPos + exitString.size(), output.size());
+ stopReaders();
+ return true;
+}
+
+bool RemoteGdbProcess::waitForInputReady(Core::InteractiveSshConnection::Ptr &conn)
+{
+ if (conn->waitForRemoteOutput(m_serverInfo.timeout).isEmpty())
+ return false;
+ while (!conn->waitForRemoteOutput(100).isEmpty())
+ ;
+ return true;
+}
+
+bool RemoteGdbProcess::sendAndWaitForEcho(Core::InteractiveSshConnection::Ptr &conn,
+ const QByteArray &cmdLine)
+{
+ conn->sendInput(cmdLine);
+ QByteArray allOutput;
+ while (!allOutput.endsWith(cmdLine)) {
+ const QByteArray curOutput = conn->waitForRemoteOutput(100);
+ if (curOutput.isEmpty())
+ return false;
+ allOutput += curOutput;
+ }
+ return true;
+}
+
+
+const QByteArray RemoteGdbProcess::CtrlC = QByteArray(1, 0x3);
+const QByteArray RemoteGdbProcess::AppOutputFile("app_output");
+const QByteArray RemoteGdbProcess::ErrOutputFile("err_output");
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** 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 REMOTEGDBPROCESS_H
+#define REMOTEGDBPROCESS_H
+
+#include "abstractgdbprocess.h"
+
+#include <coreplugin/ssh/sshconnection.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QQueue>
+
+namespace Debugger {
+namespace Internal {
+
+class RemotePlainGdbAdapter;
+
+class RemoteGdbProcess : public AbstractGdbProcess
+{
+ Q_OBJECT
+public:
+ RemoteGdbProcess(const Core::SshServerInfo &server,
+ RemotePlainGdbAdapter *adapter, QObject *parent = 0);
+
+ virtual QByteArray readAllStandardOutput();
+ virtual QByteArray readAllStandardError();
+
+ virtual void start(const QString &cmd, const QStringList &args);
+ virtual bool waitForStarted();
+ virtual qint64 write(const QByteArray &data);
+ virtual void kill();
+
+ virtual QProcess::ProcessState state() const;
+ virtual QString errorString() const;
+
+ virtual QProcessEnvironment processEnvironment() const;
+ virtual void setProcessEnvironment(const QProcessEnvironment &env);
+ virtual void setEnvironment(const QStringList &env);
+ virtual void setWorkingDirectory(const QString &dir);
+
+ static const QByteArray CtrlC;
+
+private slots:
+ void handleGdbOutput();
+ void handleAppOutput();
+ void handleErrOutput();
+
+private:
+ static QByteArray readerCmdLine(const QByteArray &file);
+
+ int findAnchor(const QByteArray &data) const;
+ qint64 sendInput(const QByteArray &data);
+ void stopReaders();
+ QByteArray removeCarriageReturn(const QByteArray &data);
+ bool checkForGdbExit(QByteArray &output);
+ bool sendAndWaitForEcho(Core::InteractiveSshConnection::Ptr &conn,
+ const QByteArray &cmdLine);
+ bool waitForInputReady(Core::InteractiveSshConnection::Ptr &conn);
+
+ static const QByteArray AppOutputFile;
+ static const QByteArray ErrOutputFile;
+
+ Core::SshServerInfo m_serverInfo;
+ Core::InteractiveSshConnection::Ptr m_gdbConn;
+ Core::InteractiveSshConnection::Ptr m_appOutputConn;
+ Core::InteractiveSshConnection::Ptr m_errOutputConn;
+ QByteArray m_gdbOutput;
+ QByteArray m_errorOutput;
+ QString m_command;
+ QStringList m_cmdArgs;
+ QString m_wd;
+ QQueue<QByteArray> m_inputToSend;
+ QByteArray m_currentGdbOutput;
+ QByteArray m_lastSeqNr;
+ QByteArray m_gdbCmdLine;
+ bool m_gdbStarted;
+
+ RemotePlainGdbAdapter *m_adapter;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // REMOTEGDBPROCESS_H
**
**************************************************************************/
-#include "remotegdbadapter.h"
+#include "remotegdbserveradapter.h"
#include "debuggerstringutils.h"
#include "gdbengine.h"
namespace Internal {
#define CB(callback) \
- static_cast<GdbEngine::AdapterCallback>(&RemoteGdbAdapter::callback), \
+ static_cast<GdbEngine::AdapterCallback>(&RemoteGdbServerAdapter::callback), \
STRINGIFY(callback)
///////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////
-RemoteGdbAdapter::RemoteGdbAdapter(GdbEngine *engine, int toolChainType, QObject *parent) :
+RemoteGdbServerAdapter::RemoteGdbServerAdapter(GdbEngine *engine, int toolChainType, QObject *parent) :
AbstractGdbAdapter(engine, parent),
m_toolChainType(toolChainType)
{
this, SLOT(readUploadStandardError()));
}
-AbstractGdbAdapter::DumperHandling RemoteGdbAdapter::dumperHandling() const
+AbstractGdbAdapter::DumperHandling RemoteGdbServerAdapter::dumperHandling() const
{
switch (m_toolChainType) {
case ProjectExplorer::ToolChain::MinGW:
return DumperLoadedByGdbPreload;
}
-void RemoteGdbAdapter::startAdapter()
+void RemoteGdbServerAdapter::startAdapter()
{
QTC_ASSERT(state() == EngineStarting, qDebug() << state());
setState(AdapterStarting);
emit adapterStarted();
}
-void RemoteGdbAdapter::uploadProcError(QProcess::ProcessError error)
+void RemoteGdbServerAdapter::uploadProcError(QProcess::ProcessError error)
{
QString msg;
switch (error) {
showMessageBox(QMessageBox::Critical, tr("Error"), msg);
}
-void RemoteGdbAdapter::readUploadStandardOutput()
+void RemoteGdbServerAdapter::readUploadStandardOutput()
{
QByteArray ba = m_uploadProc.readAllStandardOutput();
m_engine->showDebuggerOutput(LogOutput, QString::fromLocal8Bit(ba, ba.length()));
}
-void RemoteGdbAdapter::readUploadStandardError()
+void RemoteGdbServerAdapter::readUploadStandardError()
{
QByteArray ba = m_uploadProc.readAllStandardError();
m_engine->showDebuggerOutput(LogError, QString::fromLocal8Bit(ba, ba.length()));
}
-void RemoteGdbAdapter::startInferior()
+void RemoteGdbServerAdapter::startInferior()
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
CB(handleFileExecAndSymbols));
}
-void RemoteGdbAdapter::handleSetTargetAsync(const GdbResponse &response)
+void RemoteGdbServerAdapter::handleSetTargetAsync(const GdbResponse &response)
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
if (response.resultClass == GdbResultError)
qDebug() << "Adapter too old: does not support asynchronous mode.";
}
-void RemoteGdbAdapter::handleFileExecAndSymbols(const GdbResponse &response)
+void RemoteGdbServerAdapter::handleFileExecAndSymbols(const GdbResponse &response)
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
if (response.resultClass == GdbResultDone) {
}
}
-void RemoteGdbAdapter::handleTargetRemote(const GdbResponse &record)
+void RemoteGdbServerAdapter::handleTargetRemote(const GdbResponse &record)
{
QTC_ASSERT(state() == InferiorStarting, qDebug() << state());
if (record.resultClass == GdbResultDone) {
}
}
-void RemoteGdbAdapter::startInferiorPhase2()
+void RemoteGdbServerAdapter::startInferiorPhase2()
{
m_engine->continueInferiorInternal();
}
-void RemoteGdbAdapter::interruptInferior()
+void RemoteGdbServerAdapter::interruptInferior()
{
// FIXME: On some gdb versions like git 170ffa5d7dd this produces
// >810^error,msg="mi_cmd_exec_interrupt: Inferior not executing."
m_engine->postCommand("-exec-interrupt", GdbEngine::Immediate);
}
-void RemoteGdbAdapter::shutdown()
+void RemoteGdbServerAdapter::shutdown()
{
// FIXME: cleanup missing
}
#include "abstractgdbadapter.h"
+#include "abstractgdbprocess.h"
+
namespace Debugger {
namespace Internal {
//
///////////////////////////////////////////////////////////////////////
-class RemoteGdbAdapter : public AbstractGdbAdapter
+class RemoteGdbServerAdapter : public AbstractGdbAdapter
{
Q_OBJECT
public:
- RemoteGdbAdapter(GdbEngine *engine, int toolChainType, QObject *parent = 0);
+ RemoteGdbServerAdapter(GdbEngine *engine, int toolChainType, QObject *parent = 0);
virtual DumperHandling dumperHandling() const;
void startInferiorPhase2();
void interruptInferior();
void shutdown();
+ AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
private:
Q_SLOT void readUploadStandardOutput();
const int m_toolChainType;
QProcess m_uploadProc;
+ LocalGdbProcess m_gdbProc;
};
} // namespace Internal
--- /dev/null
+/**************************************************************************
+**
+** 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 "remoteplaingdbadapter.h"
+
+#include <debugger/debuggeractions.h>
+#include <debugger/debuggerstringutils.h>
+#include <utils/qtcassert.h>
+
+
+namespace Debugger {
+namespace Internal {
+
+RemotePlainGdbAdapter::RemotePlainGdbAdapter(GdbEngine *engine,
+ QObject *parent)
+ : AbstractPlainGdbAdapter(engine, parent),
+ m_gdbProc(engine->startParameters().sshserver, this)
+{
+}
+
+void RemotePlainGdbAdapter::startAdapter()
+{
+ QTC_ASSERT(state() == EngineStarting, qDebug() << state());
+ setState(AdapterStarting);
+ debugMessage(QLatin1String("TRYING TO START ADAPTER"));
+
+ if (!startParameters().workingDirectory.isEmpty())
+ m_gdbProc.setWorkingDirectory(startParameters().workingDirectory);
+ if (!startParameters().environment.isEmpty())
+ m_gdbProc.setEnvironment(startParameters().environment);
+
+ if (m_engine->startGdb(QStringList(), m_engine->startParameters().debuggerCommand))
+ emit adapterStarted();
+}
+
+void RemotePlainGdbAdapter::interruptInferior()
+{
+ m_gdbProc.write(RemoteGdbProcess::CtrlC);
+}
+
+QByteArray RemotePlainGdbAdapter::execFilePath() const
+{
+ return startParameters().executable.toUtf8();
+}
+
+bool RemotePlainGdbAdapter::infoTargetNecessary() const
+{
+ return true;
+}
+
+QByteArray RemotePlainGdbAdapter::toLocalEncoding(const QString &s) const
+{
+ return s.toUtf8();
+}
+
+QString RemotePlainGdbAdapter::fromLocalEncoding(const QByteArray &b) const
+{
+ return QString::fromUtf8(b);
+}
+
+void RemotePlainGdbAdapter::handleApplicationOutput(const QByteArray &output)
+{
+ m_engine->manager()->showApplicationOutput(output, false);
+}
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** 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 REMOTEGDBCLIENTADAPTER_H
+#define REMOTEGDBCLIENTADAPTER_H
+
+#include "abstractplaingdbadapter.h"
+#include "remotegdbprocess.h"
+
+namespace Debugger {
+namespace Internal {
+
+class RemotePlainGdbAdapter : public AbstractPlainGdbAdapter
+{
+ Q_OBJECT
+ friend class RemoteGdbProcess;
+public:
+ RemotePlainGdbAdapter(GdbEngine *engine, QObject *parent = 0);
+
+ virtual void startAdapter();
+ virtual void interruptInferior();
+ virtual AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
+ virtual DumperHandling dumperHandling() const { return DumperLoadedByGdbPreload; }
+
+private:
+ virtual QByteArray execFilePath() const;
+ virtual bool infoTargetNecessary() const;
+ virtual QByteArray toLocalEncoding(const QString &s) const;
+ virtual QString fromLocalEncoding(const QByteArray &b) const;
+ void handleApplicationOutput(const QByteArray &output);
+
+ RemoteGdbProcess m_gdbProc;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // REMOTEGDBCLIENTADAPTER_H
// m_stubProc.stop();
// m_stubProc.blockSignals(false);
- m_stubProc.setWorkingDirectory(startParameters().workingDir);
+ m_stubProc.setWorkingDirectory(startParameters().workingDirectory);
// Set environment + dumper preload.
QStringList environment = startParameters().environment;
m_stubProc.setEnvironment(environment);
#include "abstractgdbadapter.h"
+#include "abstractgdbprocess.h"
+
#include <consoleprocess.h>
namespace Debugger {
void startInferior();
void startInferiorPhase2();
void interruptInferior();
+ AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
private:
void handleStubAttached(const GdbResponse &response);
Q_SLOT void stubMessage(const QString &msg, bool isError);
Utils::ConsoleProcess m_stubProc;
+ LocalGdbProcess m_gdbProc;
};
} // namespace Internal
#include "bluetoothlistener_gui.h"
#include "registerhandler.h"
-#include "stackhandler.h"
+#include "threadshandler.h"
#include "debuggeractions.h"
#include "debuggerstringutils.h"
#include "watchutils.h"
trkReadMemoryMessage(m_session.dataseg, 12));
return;
}
- m_engine->m_gdbProc.write(data);
+ m_gdbProc.write(data);
}
uint oldPC;
#include "trkutils.h"
#include "trkdevice.h"
#include "launcher.h"
+#include "abstractgdbprocess.h"
#include <QtCore/QHash>
#include <QtCore/QPointer>
void startInferiorPhase2();
void interruptInferior();
void shutdown();
+ AbstractGdbProcess *gdbProc() { return &m_gdbProc; }
void cleanup();
void emitDelayedInferiorStartFailed(const QString &msg);
QString m_symbolFile;
int m_verbose;
bool m_bufferedMemoryRead;
+ LocalGdbProcess m_gdbProc;
};
} // namespace Internal
virtual void reloadModules() = 0;
virtual void loadSymbols(const QString &moduleName) = 0;
virtual void loadAllSymbols() = 0;
- virtual QList<Symbol> moduleSymbols(const QString &moduleName) = 0;
+ virtual void requestModuleSymbols(const QString &moduleName) = 0;
virtual void reloadRegisters() = 0;
// QAbstractItemModel
int columnCount(const QModelIndex &parent) const
- { return parent.isValid() ? 0 : 4; }
+ { return parent.isValid() ? 0 : 5; }
int rowCount(const QModelIndex &parent) const
{ return parent.isValid() ? 0 : m_modules.size(); }
QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
static QString headers[] = {
tr("Module name") + " ",
+ tr("Module path") + " ",
tr("Symbols read") + " ",
tr("Start address") + " ",
tr("End address") + " "
break;
case 1:
if (role == Qt::DisplayRole)
- return module.symbolsRead ? m_yes : m_no;
+ return module.modulePath;
break;
case 2:
if (role == Qt::DisplayRole)
- return module.startAddress;
+ return module.symbolsRead ? m_yes : m_no;
break;
case 3:
if (role == Qt::DisplayRole)
+ return module.startAddress;
+ break;
+ case 4:
+ if (role == Qt::DisplayRole)
return module.endAddress;
break;
}
public:
QString moduleName;
+ QString modulePath;
bool symbolsRead;
QString startAddress;
QString endAddress;
resizeColumnToContents(0);
resizeColumnToContents(1);
resizeColumnToContents(2);
+ resizeColumnToContents(3);
}
void ModulesWindow::setAlwaysResizeColumnsToContents(bool on)
header()->setResizeMode(1, mode);
header()->setResizeMode(2, mode);
header()->setResizeMode(3, mode);
+ header()->setResizeMode(4, mode);
//setColumnHidden(3, true);
}
void ModulesWindow::showSymbols(const QString &name)
{
- if (name.isEmpty())
- return;
- QApplication::setOverrideCursor(Qt::WaitCursor);
- const QList<Symbol> symbols = m_debuggerManager->moduleSymbols(name);
- QApplication::restoreOverrideCursor();
- if (symbols.empty())
- return;
- QTreeWidget *w = new QTreeWidget;
- w->setColumnCount(3);
- w->setRootIsDecorated(false);
- w->setAlternatingRowColors(true);
- w->setHeaderLabels(QStringList() << tr("Address") << tr("Code") << tr("Symbol"));
- w->setWindowTitle(tr("Symbols in \"%1\"").arg(name));
- foreach (const Symbol &s, symbols) {
- QTreeWidgetItem *it = new QTreeWidgetItem;
- it->setData(0, Qt::DisplayRole, s.address);
- it->setData(1, Qt::DisplayRole, s.state);
- it->setData(2, Qt::DisplayRole, s.name);
- w->addTopLevelItem(it);
- }
- emit newDockRequested(w);
+ if (!name.isEmpty())
+ m_debuggerManager->requestModuleSymbols(name);
}
} // namespace Internal
void PdbEngine::reloadModules()
{
+ postCommand("qdebug('listmodules')", CB(handleListModules));
+}
+
+void PdbEngine::handleListModules(const PdbResponse &response)
+{
+ GdbMi out;
+ out.fromString(response.data.trimmed());
+ QList<Module> modules;
+ foreach (const GdbMi &item, out.children()) {
+ Module module;
+ module.moduleName = _(item.findChild("name").data());
+ QString path = _(item.findChild("value").data());
+ int pos = path.indexOf(_("' from '"));
+ if (pos != -1) {
+ path = path.mid(pos + 8);
+ if (path.size() >= 2)
+ path.chop(2);
+ } else if (path.startsWith(_("<module '"))
+ && path.endsWith(_("' (built-in)>"))) {
+ path = _("(builtin)");
+ }
+ module.modulePath = path;
+ modules.append(module);
+ }
+ manager()->modulesHandler()->setModules(modules);
}
-QList<Symbol> PdbEngine::moduleSymbols(const QString & /*moduleName*/)
+void PdbEngine::requestModuleSymbols(const QString &moduleName)
{
- return QList<Symbol>();
+ postCommand("qdebug('listsymbols','" + moduleName.toLatin1() + "')",
+ CB(handleListSymbols), moduleName);
}
+void PdbEngine::handleListSymbols(const PdbResponse &response)
+{
+ GdbMi out;
+ out.fromString(response.data.trimmed());
+ QList<Symbol> symbols;
+ QString moduleName = response.cookie.toString();
+ foreach (const GdbMi &item, out.children()) {
+ Symbol symbol;
+ symbol.name = _(item.findChild("name").data());
+ symbols.append(symbol);
+ }
+ manager()->showModuleSymbols(moduleName, symbols);
+}
//////////////////////////////////////////////////////////////////////
//
showDebuggerOutput(LogDebug, msg);
}
+unsigned PdbEngine::debuggerCapabilities() const
+{
+ return ReloadModuleCapability;
+}
+
IDebuggerEngine *createPdbEngine(DebuggerManager *manager)
{
return new PdbEngine(manager);
void loadSymbols(const QString &moduleName);
void loadAllSymbols();
- virtual QList<Symbol> moduleSymbols(const QString &moduleName);
+ void requestModuleSymbols(const QString &moduleName);
void reloadModules();
void reloadRegisters() {}
void reloadSourceFiles() {}
private:
void debugMessage(const QString &msg);
QString errorMessage(QProcess::ProcessError error) const;
+ unsigned debuggerCapabilities() const;
Q_SLOT void handlePdbFinished(int, QProcess::ExitStatus status);
Q_SLOT void handlePdbError(QProcess::ProcessError error);
void handleStop(const PdbResponse &response);
void handleBacktrace(const PdbResponse &response);
void handleListLocals(const PdbResponse &response);
+ void handleListModules(const PdbResponse &response);
+ void handleListSymbols(const PdbResponse &response);
void handleLoadDumper(const PdbResponse &response);
void handleBreakInsert(const PdbResponse &response);
{
}
-QList<Symbol> ScriptEngine::moduleSymbols(const QString & /*moduleName*/)
+void ScriptEngine::requestModuleSymbols(const QString & /*moduleName*/)
{
- return QList<Symbol>();
}
void loadSymbols(const QString &moduleName);
void loadAllSymbols();
- virtual QList<Symbol> moduleSymbols(const QString &moduleName);
+ void requestModuleSymbols(const QString &moduleName);
void reloadModules();
void reloadRegisters() {}
void reloadSourceFiles() {}
--- /dev/null
+/**************************************************************************
+**
+** 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 "stackframe.h"
+#include "stackhandler.h"
+
+#include <QtCore/QFileInfo>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QTextStream>
+
+namespace Debugger {
+namespace Internal {
+
+////////////////////////////////////////////////////////////////////////
+//
+// StackFrame
+//
+////////////////////////////////////////////////////////////////////////
+
+StackFrame::StackFrame()
+ : level(0), line(0)
+{}
+
+void StackFrame::clear()
+{
+ line = level = 0;
+ function.clear();
+ file.clear();
+ from.clear();
+ to.clear();
+ address.clear();
+}
+
+bool StackFrame::isUsable() const
+{
+ return !file.isEmpty() && QFileInfo(file).isReadable();
+}
+
+QString StackFrame::toString() const
+{
+ QString res;
+ QTextStream str(&res);
+ str << StackHandler::tr("Address:") << ' ' << address << ' '
+ << StackHandler::tr("Function:") << ' ' << function << ' '
+ << StackHandler::tr("File:") << ' ' << file << ' '
+ << StackHandler::tr("Line:") << ' ' << line << ' '
+ << StackHandler::tr("From:") << ' ' << from << ' '
+ << StackHandler::tr("To:") << ' ' << to;
+ return res;
+}
+
+QString StackFrame::toToolTip() const
+{
+ QString res;
+ QTextStream str(&res);
+ str << "<html><body><table>"
+ << "<tr><td>" << StackHandler::tr("Address:") << "</td><td>" << address << "</td></tr>"
+ << "<tr><td>" << StackHandler::tr("Function:") << "</td><td>" << function << "</td></tr>"
+ << "<tr><td>" << StackHandler::tr("File:") << "</td><td>" << QDir::toNativeSeparators(file) << "</td></tr>"
+ << "<tr><td>" << StackHandler::tr("Line:") << "</td><td>" << line << "</td></tr>"
+ << "<tr><td>" << StackHandler::tr("From:") << "</td><td>" << from << "</td></tr>"
+ << "<tr><td>" << StackHandler::tr("To:") << "</td><td>" << to << "</td></tr>"
+ << "</table></body></html>";
+ return res;
+}
+
+QDebug operator<<(QDebug d, const StackFrame &f)
+{
+ QString res;
+ QTextStream str(&res);
+ str << "level=" << f.level << " address=" << f.address;
+ if (!f.function.isEmpty())
+ str << ' ' << f.function;
+ if (!f.file.isEmpty())
+ str << ' ' << f.file << ':' << f.line;
+ if (!f.from.isEmpty())
+ str << " from=" << f.from;
+ if (!f.to.isEmpty())
+ str << " to=" << f.to;
+ d.nospace() << res;
+ return d;
+}
+
+} // namespace Internal
+} // namespace Debugger
namespace Debugger {
namespace Internal {
-StackFrame::StackFrame()
- : level(0), line(0)
-{}
-
-void StackFrame::clear()
-{
- line = level = 0;
- function.clear();
- file.clear();
- from.clear();
- to.clear();
- address.clear();
-}
-
-bool StackFrame::isUsable() const
-{
- return !file.isEmpty() && QFileInfo(file).isReadable();
-}
-
-QString StackFrame::toString() const
-{
- QString res;
- QTextStream str(&res);
- str << StackHandler::tr("Address:") << ' ' << address << ' '
- << StackHandler::tr("Function:") << ' ' << function << ' '
- << StackHandler::tr("File:") << ' ' << file << ' '
- << StackHandler::tr("Line:") << ' ' << line << ' '
- << StackHandler::tr("From:") << ' ' << from << ' '
- << StackHandler::tr("To:") << ' ' << to;
- return res;
-}
-
-QString StackFrame::toToolTip() const
-{
- QString res;
- QTextStream str(&res);
- str << "<html><body><table>"
- << "<tr><td>" << StackHandler::tr("Address:") << "</td><td>" << address << "</td></tr>"
- << "<tr><td>" << StackHandler::tr("Function:") << "</td><td>" << function << "</td></tr>"
- << "<tr><td>" << StackHandler::tr("File:") << "</td><td>" << QDir::toNativeSeparators(file) << "</td></tr>"
- << "<tr><td>" << StackHandler::tr("Line:") << "</td><td>" << line << "</td></tr>"
- << "<tr><td>" << StackHandler::tr("From:") << "</td><td>" << from << "</td></tr>"
- << "<tr><td>" << StackHandler::tr("To:") << "</td><td>" << to << "</td></tr>"
- << "</table></body></html>";
- return res;
-}
-
-QDebug operator<<(QDebug d, const StackFrame &f)
-{
- QString res;
- QTextStream str(&res);
- str << "level=" << f.level << " address=" << f.address;
- if (!f.function.isEmpty())
- str << ' ' << f.function;
- if (!f.file.isEmpty())
- str << ' ' << f.file << ':' << f.line;
- if (!f.from.isEmpty())
- str << " from=" << f.from;
- if (!f.to.isEmpty())
- str << " to=" << f.to;
- d.nospace() << res;
- return d;
-}
////////////////////////////////////////////////////////////////////////
//
}
-////////////////////////////////////////////////////////////////////////
-//
-// ThreadsHandler
-//
-////////////////////////////////////////////////////////////////////////
-
-ThreadData::ThreadData(int threadId) :
- id(threadId),
- address(0),
- line(-1)
-{
-}
-
-void ThreadData::notifyRunning()
-{
- address = 0;
- function.clear();
- file.clear();
- line = -1;
-}
-
-enum { IdColumn, AddressColumn, FunctionColumn, FileColumn, LineColumn, ColumnCount };
-
-ThreadsHandler::ThreadsHandler(QObject *parent) :
- QAbstractTableModel(parent),
- m_currentIndex(0),
- m_positionIcon(QLatin1String(":/debugger/images/location_16.png")),
- m_emptyIcon(QLatin1String(":/debugger/images/debugger_empty_14.png"))
-{
-}
-
-int ThreadsHandler::rowCount(const QModelIndex &parent) const
-{
- // Since the stack is not a tree, row count is 0 for any valid parent
- return parent.isValid() ? 0 : m_threads.size();
-}
-
-int ThreadsHandler::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : int(ColumnCount);
-}
-
-QVariant ThreadsHandler::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return QVariant();
- const int row = index.row();
- if (row >= m_threads.size())
- return QVariant();
- const ThreadData &thread = m_threads.at(row);
-
- if (role == Qt::DisplayRole) {
- switch (index.column()) {
- case IdColumn:
- return thread.id;
- case FunctionColumn:
- return thread.function;
- case FileColumn:
- return thread.file;
- case LineColumn:
- return thread.line >= 0 ? QString::number(thread.line) : QString();
- case AddressColumn:
- return thread.address > 0 ? QLatin1String("0x") + QString::number(thread.address, 16) : QString();
- }
- } else if (role == Qt::ToolTipRole) {
- if (thread.address == 0)
- return tr("Thread: %1").arg(thread.id);
- // Stopped
- if (thread.file.isEmpty())
- return tr("Thread: %1 at %2 (0x%3)").arg(thread.id).arg(thread.function).arg(thread.address, 0, 16);
- return tr("Thread: %1 at %2, %3:%4 (0x%5)").
- arg(thread.id).arg(thread.function, thread.file).arg(thread.line).arg(thread.address, 0, 16);
- } else if (role == Qt::DecorationRole && index.column() == 0) {
- // Return icon that indicates whether this is the active stack frame
- return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon;
- }
-
- return QVariant();
-}
-
-QVariant ThreadsHandler::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
- return QVariant();
- switch (section) {
- case IdColumn:
- return tr("Thread ID");
- case FunctionColumn:
- return tr("Function");
- case FileColumn:
- return tr("File");
- case LineColumn:
- return tr("Line");
- case AddressColumn:
- return tr("Address");
- }
- return QVariant();
-}
-
-void ThreadsHandler::setCurrentThread(int index)
-{
- if (index == m_currentIndex)
- return;
-
- // Emit changed for previous frame
- QModelIndex i = ThreadsHandler::index(m_currentIndex, 0);
- emit dataChanged(i, i);
-
- m_currentIndex = index;
-
- // Emit changed for new frame
- i = ThreadsHandler::index(m_currentIndex, 0);
- emit dataChanged(i, i);
-}
-
-void ThreadsHandler::setThreads(const QList<ThreadData> &threads)
-{
- m_threads = threads;
- if (m_currentIndex >= m_threads.size())
- m_currentIndex = m_threads.size() - 1;
- reset();
-}
-
-QList<ThreadData> ThreadsHandler::threads() const
-{
- return m_threads;
-}
-
-void ThreadsHandler::removeAll()
-{
- m_threads.clear();
- m_currentIndex = 0;
- reset();
-}
-
-void ThreadsHandler::notifyRunning()
-{
- // Threads stopped (that is, address != 0 showing)?
- if (m_threads.empty())
- return;
- if (m_threads.front().address == 0)
- return;
- const QList<ThreadData>::iterator end = m_threads.end();
- for (QList<ThreadData>::iterator it = m_threads.begin(); it != end; ++it)
- it->notifyRunning();
- emit dataChanged(index(0, 1), index(m_threads.size()- 1, ColumnCount - 1));
-}
-
} // namespace Internal
} // namespace Debugger
namespace Debugger {
namespace Internal {
+////////////////////////////////////////////////////////////////////////
+//
+// StackModel
+//
+////////////////////////////////////////////////////////////////////////
+
struct StackCookie
{
StackCookie() : isFull(true), gotoLocation(false) {}
};
-////////////////////////////////////////////////////////////////////////
-//
-// ThreadsHandler
-//
-////////////////////////////////////////////////////////////////////////
-
-struct ThreadData
-{
- ThreadData(int threadId = 0);
- void notifyRunning(); // Clear state information
-
- int id;
- // State information when stopped
- quint64 address;
- QString function;
- QString file;
- int line;
-};
-
-/*! A model to represent the running threads in a QTreeView or ComboBox */
-class ThreadsHandler : public QAbstractTableModel
-{
- Q_OBJECT
-
-public:
- ThreadsHandler(QObject *parent = 0);
-
- void setCurrentThread(int index);
- void selectThread(int index);
- void setThreads(const QList<ThreadData> &threads);
- void removeAll();
- QList<ThreadData> threads() const;
- QAbstractItemModel *threadsModel() { return this; }
-
- // Clear out all frame information
- void notifyRunning();
-
-private:
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- int columnCount(const QModelIndex &parent = QModelIndex()) const;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
-
-private:
- QList<ThreadData> m_threads;
- int m_currentIndex;
- const QIcon m_positionIcon;
- const QIcon m_emptyIcon;
-};
-
} // namespace Internal
} // namespace Debugger
</property>
</widget>
</item>
+ <item row="0" column="1">
+ <widget class="Utils::PathChooser" name="execFile" native="true"/>
+ </item>
<item row="1" column="0">
<widget class="QLabel" name="argLabel">
<property name="text">
<item row="1" column="1">
<widget class="QLineEdit" name="argsEdit"/>
</item>
- <item row="0" column="1">
- <widget class="Utils::PathChooser" name="execFile" native="true"/>
+ <item row="2" column="0">
+ <widget class="QLabel" name="workingDirectoryLabel">
+ <property name="text">
+ <string>Working directory:</string>
+ </property>
+ </widget>
</item>
<item row="2" column="1">
+ <widget class="Utils::PathChooser" name="workingDirectory" native="true"/>
+ </item>
+ <item row="3" column="1">
<widget class="QCheckBox" name="checkBoxBreakAtMain">
<property name="text">
<string/>
</property>
</widget>
</item>
- <item row="2" column="0">
+ <item row="3" column="0">
<widget class="QLabel" name="labelBreakAtMain">
<property name="text">
<string>Break at 'main':</string>
--- /dev/null
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 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 "json.h"
+
+#include <utils/qtcassert.h>
+
+#include <QtCore/QByteArray>
+#include <QtCore/QTextStream>
+
+#include <ctype.h>
+
+//#define DEBUG_JASON
+#ifdef DEBUG_JASON
+#define JDEBUG(s) qDebug() << s
+#else
+#define JDEBUG(s)
+#endif
+
+namespace Debugger {
+namespace Internal {
+
+static void skipSpaces(const char *&from, const char *to)
+{
+ while (from != to && isspace(*from))
+ ++from;
+}
+
+QTextStream &operator<<(QTextStream &os, const JsonValue &mi)
+{
+ return os << mi.toString();
+}
+
+void JsonValue::parsePair(const char *&from, const char *to)
+{
+ skipSpaces(from, to);
+ JDEBUG("parsePair: " << QByteArray(from, to - from));
+ m_name = parseCString(from, to);
+ skipSpaces(from, to);
+ while (from < to && *from != ':') {
+ JDEBUG("not a colon" << *from);
+ ++from;
+ }
+ ++from;
+ parseValue(from, to);
+ skipSpaces(from, to);
+}
+
+QByteArray JsonValue::parseNumber(const char *&from, const char *to)
+{
+ QByteArray result;
+ while (from < to && *from >= '0' && *from <= '9')
+ result.append(*from++);
+ return result;
+}
+
+QByteArray JsonValue::parseCString(const char *&from, const char *to)
+{
+ QByteArray result;
+ JDEBUG("parseCString: " << QByteArray(from, to - from));
+ if (*from != '"') {
+ qDebug() << "JSON Parse Error, double quote expected";
+ ++from; // So we don't hang
+ return QByteArray();
+ }
+ const char *ptr = from;
+ ++ptr;
+ while (ptr < to) {
+ if (*ptr == '"') {
+ ++ptr;
+ result = QByteArray(from + 1, ptr - from - 2);
+ break;
+ }
+ if (*ptr == '\\') {
+ ++ptr;
+ if (ptr == to) {
+ qDebug() << "JSON Parse Error, unterminated backslash escape";
+ from = ptr; // So we don't hang
+ return QByteArray();
+ }
+ }
+ ++ptr;
+ }
+ from = ptr;
+
+ int idx = result.indexOf('\\');
+ if (idx >= 0) {
+ char *dst = result.data() + idx;
+ const char *src = dst + 1, *end = result.data() + result.length();
+ do {
+ char c = *src++;
+ switch (c) {
+ case 'a': *dst++ = '\a'; break;
+ case 'b': *dst++ = '\b'; break;
+ case 'f': *dst++ = '\f'; break;
+ case 'n': *dst++ = '\n'; break;
+ case 'r': *dst++ = '\r'; break;
+ case 't': *dst++ = '\t'; break;
+ case 'v': *dst++ = '\v'; break;
+ case '"': *dst++ = '"'; break;
+ case '\\': *dst++ = '\\'; break;
+ default:
+ {
+ int chars = 0;
+ uchar prod = 0;
+ forever {
+ if (c < '0' || c > '7') {
+ --src;
+ break;
+ }
+ prod = prod * 8 + c - '0';
+ if (++chars == 3 || src == end)
+ break;
+ c = *src++;
+ }
+ if (!chars) {
+ qDebug() << "JSON Parse Error, unrecognized backslash escape";
+ return QByteArray();
+ }
+ *dst++ = prod;
+ }
+ }
+ while (src != end) {
+ char c = *src++;
+ if (c == '\\')
+ break;
+ *dst++ = c;
+ }
+ } while (src != end);
+ *dst = 0;
+ result.truncate(dst - result.data());
+ }
+
+ JDEBUG("parseCString, got " << result);
+ return result;
+}
+
+void JsonValue::parseValue(const char *&from, const char *to)
+{
+ JDEBUG("parseValue: " << QByteArray(from, to - from));
+ switch (*from) {
+ case '{':
+ parseObject(from, to);
+ break;
+ case '[':
+ parseArray(from, to);
+ break;
+ case '"':
+ m_type = String;
+ m_data = parseCString(from, to);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ m_type = Number;
+ m_data = parseNumber(from, to);
+ default:
+ break;
+ }
+}
+
+void JsonValue::parseObject(const char *&from, const char *to)
+{
+ JDEBUG("parseObject: " << QByteArray(from, to - from));
+ QTC_ASSERT(*from == '{', /**/);
+ ++from;
+ m_type = Object;
+ while (from < to) {
+ if (*from == '}') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parsePair(from, to);
+ if (!child.isValid())
+ return;
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::parseArray(const char *&from, const char *to)
+{
+ JDEBUG("parseArray: " << QByteArray(from, to - from));
+ QTC_ASSERT(*from == '[', /**/);
+ ++from;
+ m_type = Array;
+ while (from < to) {
+ if (*from == ']') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parseValue(from, to);
+ if (child.isValid())
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::setStreamOutput(const QByteArray &name, const QByteArray &content)
+{
+ if (content.isEmpty())
+ return;
+ JsonValue child;
+ child.m_type = String;
+ child.m_name = name;
+ child.m_data = content;
+ m_children += child;
+ if (m_type == Invalid)
+ m_type = Object;
+}
+
+static QByteArray ind(int indent)
+{
+ return QByteArray(2 * indent, ' ');
+}
+
+void JsonValue::dumpChildren(QByteArray * str, bool multiline, int indent) const
+{
+ for (int i = 0; i < m_children.size(); ++i) {
+ if (i != 0) {
+ *str += ',';
+ if (multiline)
+ *str += '\n';
+ }
+ if (multiline)
+ *str += ind(indent);
+ *str += m_children.at(i).toString(multiline, indent);
+ }
+}
+
+class MyString : public QString {
+public:
+ ushort at(int i) const { return constData()[i].unicode(); }
+};
+
+template<class ST, typename CT>
+inline ST escapeCStringTpl(const ST &ba)
+{
+ ST ret;
+ ret.reserve(ba.length() * 2);
+ for (int i = 0; i < ba.length(); ++i) {
+ CT c = ba.at(i);
+ switch (c) {
+ case '\\': ret += "\\\\"; break;
+ case '\a': ret += "\\a"; break;
+ case '\b': ret += "\\b"; break;
+ case '\f': ret += "\\f"; break;
+ case '\n': ret += "\\n"; break;
+ case '\r': ret += "\\r"; break;
+ case '\t': ret += "\\t"; break;
+ case '\v': ret += "\\v"; break;
+ case '"': ret += "\\\""; break;
+ default:
+ if (c < 32 || c == 127) {
+ ret += '\\';
+ ret += '0' + (c >> 6);
+ ret += '0' + ((c >> 3) & 7);
+ ret += '0' + (c & 7);
+ } else {
+ ret += c;
+ }
+ }
+ }
+ return ret;
+}
+
+QString JsonValue::escapeCString(const QString &ba)
+{
+ return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
+}
+
+QByteArray JsonValue::escapeCString(const QByteArray &ba)
+{
+ return escapeCStringTpl<QByteArray, uchar>(ba);
+}
+
+QByteArray JsonValue::toString(bool multiline, int indent) const
+{
+ QByteArray result;
+ switch (m_type) {
+ case Invalid:
+ if (multiline)
+ result += ind(indent) + "Invalid\n";
+ else
+ result += "Invalid";
+ break;
+ case String:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ result += '"' + escapeCString(m_data) + '"';
+ break;
+ case Number:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Object:
+ if (!m_name.isEmpty())
+ result += m_name + '=';
+ if (multiline) {
+ result += "{\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "}";
+ } else {
+ result += "{";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "}";
+ }
+ break;
+ case Array:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "[\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "]";
+ } else {
+ result += "[";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "]";
+ }
+ break;
+ }
+ return result;
+}
+
+void JsonValue::fromString(const QByteArray &ba)
+{
+ const char *from = ba.constBegin();
+ const char *to = ba.constEnd();
+ parseValue(from, to);
+}
+
+JsonValue JsonValue::findChild(const char *name) const
+{
+ for (int i = 0; i < m_children.size(); ++i)
+ if (m_children.at(i).m_name == name)
+ return m_children.at(i);
+ return JsonValue();
+}
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 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 DEBUGGER_JSON_H
+#define DEBUGGER_JSON_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QList>
+
+namespace Debugger {
+namespace Internal {
+
+class JsonValue
+{
+public:
+ JsonValue() : m_type(Invalid) {}
+ explicit JsonValue(const QByteArray &str) { fromString(str); }
+
+ QByteArray m_name;
+ QByteArray m_data;
+ QList<JsonValue> m_children;
+
+ enum Type {
+ Invalid,
+ String,
+ Number,
+ Object,
+ Array,
+ };
+
+ Type m_type;
+
+ inline Type type() const { return m_type; }
+ inline QByteArray name() const { return m_name; }
+ inline bool hasName(const char *name) const { return m_name == name; }
+
+ inline bool isValid() const { return m_type != Invalid; }
+ inline bool isNumber() const { return m_type == Number; }
+ inline bool isString() const { return m_type == String; }
+ inline bool isObject() const { return m_type == Object; }
+ inline bool isArray() const { return m_type == Array; }
+
+
+ inline QByteArray data() const { return m_data; }
+ inline const QList<JsonValue> &children() const { return m_children; }
+ inline int childCount() const { return m_children.size(); }
+
+ const JsonValue &childAt(int index) const { return m_children[index]; }
+ JsonValue &childAt(int index) { return m_children[index]; }
+ JsonValue findChild(const char *name) const;
+
+ QByteArray toString(bool multiline = false, int indent = 0) const;
+ void fromString(const QByteArray &str);
+ void setStreamOutput(const QByteArray &name, const QByteArray &content);
+
+private:
+ static QByteArray parseCString(const char *&from, const char *to);
+ static QByteArray parseNumber(const char *&from, const char *to);
+ static QByteArray escapeCString(const QByteArray &ba);
+ static QString escapeCString(const QString &ba);
+ void parsePair(const char *&from, const char *to);
+ void parseValue(const char *&from, const char *to);
+ void parseObject(const char *&from, const char *to);
+ void parseArray(const char *&from, const char *to);
+
+ void dumpChildren(QByteArray *str, bool multiline, int indent) const;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+//Q_DECLARE_METATYPE(GdbDebugger::Internal::JsonValue);
+
+#endif // DEBUGGER_JSON_H
--- /dev/null
+HEADERS += \
+ $$PWD/json.h \
+ $$PWD/tcfengine.h \
+
+SOURCES += \
+ $$PWD/json.cpp \
+ $$PWD/tcfengine.cpp \
+
+FORMS +=
+
+RESOURCES +=
--- /dev/null
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 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 "tcfengine.h"
+
+#include "debuggerstringutils.h"
+#include "debuggerdialogs.h"
+#include "breakhandler.h"
+#include "debuggerconstants.h"
+#include "debuggermanager.h"
+#include "moduleshandler.h"
+#include "registerhandler.h"
+#include "stackhandler.h"
+#include "watchhandler.h"
+#include "watchutils.h"
+#include "moduleshandler.h"
+#include "json.h"
+
+#include <utils/qtcassert.h>
+
+#include <QtCore/QDateTime>
+#include <QtCore/QDebug>
+#include <QtCore/QDir>
+#include <QtCore/QFileInfo>
+#include <QtCore/QTimer>
+
+#include <QtGui/QAction>
+#include <QtGui/QApplication>
+#include <QtGui/QMainWindow>
+#include <QtGui/QMessageBox>
+#include <QtGui/QToolTip>
+
+#include <QtNetwork/QTcpSocket>
+
+#define DEBUG_TCF 1
+#if DEBUG_TCF
+# define SDEBUG(s) qDebug() << s
+#else
+# define SDEBUG(s)
+#endif
+# define XSDEBUG(s) qDebug() << s
+
+#define CB(callback) &TcfEngine::callback, STRINGIFY(callback)
+
+//#define USE_CONGESTION_CONTROL
+
+static QByteArray C(const QByteArray &ba1,
+ const QByteArray &ba2 = QByteArray(),
+ const QByteArray &ba3 = QByteArray(),
+ const QByteArray &ba4 = QByteArray(),
+ const QByteArray &ba5 = QByteArray())
+{
+ QByteArray result = ba1;
+ if (!ba2.isEmpty()) { result += '\0'; result += ba2; }
+ if (!ba3.isEmpty()) { result += '\0'; result += ba3; }
+ if (!ba4.isEmpty()) { result += '\0'; result += ba4; }
+ if (!ba5.isEmpty()) { result += '\0'; result += ba5; }
+ return result;
+}
+
+namespace Debugger {
+namespace Internal {
+
+///////////////////////////////////////////////////////////////////////
+//
+// TcfCommand
+//
+///////////////////////////////////////////////////////////////////////
+
+
+QString TcfEngine::TcfCommand::toString() const
+{
+ return quoteUnprintableLatin1(command);
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// TcfEngine
+//
+///////////////////////////////////////////////////////////////////////
+
+TcfEngine::TcfEngine(DebuggerManager *manager)
+ : IDebuggerEngine(manager)
+{
+ m_congestion = 0;
+ m_inAir = 0;
+
+ m_sendTimer.setSingleShot(true);
+ m_sendTimer.setInterval(100); // ms
+ connect(&m_sendTimer, SIGNAL(timeout()), this, SLOT(handleSendTimer()));
+
+ m_socket = new QTcpSocket(this);
+ connect(m_socket, SIGNAL(connected()), this, SLOT(socketConnected()));
+ connect(m_socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
+ connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(socketError(QAbstractSocket::SocketError)));
+
+ //void aboutToClose ()
+ //void bytesWritten ( qint64 bytes )
+ //void readChannelFinished ()
+ connect(m_socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()));
+
+ //connect(m_socket, SIGNAL(hostFound())
+ //connect(m_socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy, QAuthenticator *)))
+ //connect(m_socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
+ // thism SLOT(socketStateChanged(QAbstractSocket::SocketState)));
+}
+
+TcfEngine::~TcfEngine()
+{
+}
+
+void TcfEngine::socketReadyRead()
+{
+ //XSDEBUG("TcfEngine::socketReadyRead()");
+ m_inbuffer.append(m_socket->readAll());
+ int pos = 0;
+ while (1) {
+ // the "\3" is followed by either "\1" or "\2"
+ int next = m_inbuffer.indexOf("\3", pos);
+ //qDebug() << "pos: " << pos << "next: " << next;
+ if (next == -1)
+ break;
+ handleResponse(m_inbuffer.mid(pos, next - pos));
+ pos = next + 2;
+ }
+ m_inbuffer.clear();
+}
+
+void TcfEngine::socketConnected()
+{
+ showStatusMessage("Socket connected.");
+ m_socket->waitForConnected(2000);
+ //sendCommand("Locator", "redirect", "ID");
+}
+
+void TcfEngine::socketDisconnected()
+{
+ XSDEBUG("FIXME: TcfEngine::socketDisconnected()");
+}
+
+void TcfEngine::socketError(QAbstractSocket::SocketError)
+{
+ QString msg = tr("%1.").arg(m_socket->errorString());
+ //QMessageBox::critical(q->mainWindow(), tr("Error"), msg);
+ showStatusMessage(msg);
+ manager()->notifyInferiorExited();
+}
+
+void TcfEngine::executeDebuggerCommand(const QString &command)
+{
+ QByteArray cmd = command.toUtf8();
+ cmd = cmd.mid(cmd.indexOf(' ') + 1);
+ QByteArray null;
+ null.append('\0');
+ // FIXME: works for single-digit escapes only
+ cmd.replace("\\0", null);
+ cmd.replace("\\1", "\1");
+ cmd.replace("\\3", "\3");
+ TcfCommand tcf;
+ tcf.command = cmd;
+ enqueueCommand(tcf);
+}
+
+void TcfEngine::shutdown()
+{
+ m_congestion = 0;
+ m_inAir = 0;
+ m_services.clear();
+ exitDebugger();
+}
+
+void TcfEngine::exitDebugger()
+{
+ SDEBUG("TcfEngine::exitDebugger()");
+ manager()->notifyInferiorExited();
+}
+
+void TcfEngine::startDebugger(const DebuggerStartParametersPtr &sp)
+{
+ setState(InferiorRunningRequested);
+ showStatusMessage(tr("Running requested..."), 5000);
+ const int pos = sp->remoteChannel.indexOf(QLatin1Char(':'));
+ const QString host = sp->remoteChannel.left(pos);
+ const quint16 port = sp->remoteChannel.mid(pos + 1).toInt();
+ //QTimer::singleShot(0, this, SLOT(runInferior()));
+ m_socket->connectToHost(host, port);
+ emit startSuccessful();
+}
+
+void TcfEngine::continueInferior()
+{
+ SDEBUG("TcfEngine::continueInferior()");
+}
+
+void TcfEngine::runInferior()
+{
+}
+
+void TcfEngine::interruptInferior()
+{
+ XSDEBUG("TcfEngine::interruptInferior()");
+}
+
+void TcfEngine::executeStep()
+{
+ //SDEBUG("TcfEngine::executeStep()");
+}
+
+void TcfEngine::executeStepI()
+{
+ //SDEBUG("TcfEngine::executeStepI()");
+}
+
+void TcfEngine::executeStepOut()
+{
+ //SDEBUG("TcfEngine::executeStepOut()");
+}
+
+void TcfEngine::executeNext()
+{
+ //SDEBUG("TcfEngine::nextExec()");
+}
+
+void TcfEngine::executeNextI()
+{
+ //SDEBUG("TcfEngine::executeNextI()");
+}
+
+void TcfEngine::executeRunToLine(const QString &fileName, int lineNumber)
+{
+ Q_UNUSED(fileName)
+ Q_UNUSED(lineNumber)
+ SDEBUG("FIXME: TcfEngine::executeRunToLine()");
+}
+
+void TcfEngine::executeRunToFunction(const QString &functionName)
+{
+ Q_UNUSED(functionName)
+ XSDEBUG("FIXME: TcfEngine::executeRunToFunction()");
+}
+
+void TcfEngine::executeJumpToLine(const QString &fileName, int lineNumber)
+{
+ Q_UNUSED(fileName)
+ Q_UNUSED(lineNumber)
+ XSDEBUG("FIXME: TcfEngine::executeJumpToLine()");
+}
+
+void TcfEngine::activateFrame(int index)
+{
+ Q_UNUSED(index)
+}
+
+void TcfEngine::selectThread(int index)
+{
+ Q_UNUSED(index)
+}
+
+void TcfEngine::attemptBreakpointSynchronization()
+{
+}
+
+void TcfEngine::loadSymbols(const QString &moduleName)
+{
+ Q_UNUSED(moduleName)
+}
+
+void TcfEngine::loadAllSymbols()
+{
+}
+
+void TcfEngine::reloadModules()
+{
+}
+
+void TcfEngine::requestModuleSymbols(const QString &moduleName)
+{
+ Q_UNUSED(moduleName)
+}
+
+
+void TcfEngine::handleResponse(const QByteArray &response)
+{
+ static QTime lastTime;
+
+ //debugMessage(_(" "), currentTime());
+ QList<QByteArray> parts = response.split('\0');
+ if (parts.size() < 2 || !parts.last().isEmpty()) {
+ SDEBUG("WRONG RESPONSE PACKET LAYOUT" << parts);
+ //if (response.isEmpty())
+ acknowledgeResult();
+ return;
+ }
+ parts.removeLast(); // always empty
+ QByteArray tag = parts.at(0);
+ int n = parts.size();
+ if (n == 2 && tag == "N") { // unidentified command
+ int token = parts.at(1).toInt();
+ TcfCommand tcf = m_cookieForToken[token];
+ SDEBUG("COMMAND NOT RECOGNIZED FOR TOKEN" << token << tcf.toString());
+ showDebuggerOutput(LogOutput, QString::number(token) + "^"
+ + "NOT RECOQNIZED: " + quoteUnprintableLatin1(response));
+ acknowledgeResult();
+ } else if (n == 2 && tag == "F") { // flow control
+ m_congestion = parts.at(1).toInt();
+ SDEBUG("CONGESTION: " << m_congestion);
+ } else if (n == 4 && tag == "R") { // result data
+ acknowledgeResult();
+ int token = parts.at(1).toInt();
+ QByteArray message = parts.at(2);
+ JsonValue data(parts.at(3));
+ showDebuggerOutput(LogOutput, QString("%1^%2%3").arg(token)
+ .arg(quoteUnprintableLatin1(response))
+ .arg(QString::fromUtf8(data.toString())));
+ TcfCommand tcf = m_cookieForToken[token];
+ JsonValue result(data);
+ SDEBUG("GOOD RESPONSE: " << quoteUnprintableLatin1(response));
+ if (tcf.callback)
+ (this->*(tcf.callback))(result, tcf.cookie);
+ } else if (n == 3 && tag == "P") { // progress data (partial result)
+ //int token = parts.at(1).toInt();
+ QByteArray data = parts.at(2);
+ SDEBUG(_("\nTCF PARTIAL:") << quoteUnprintableLatin1(response));
+ } else if (n == 4 && tag == "E") { // an event
+ QByteArray service = parts.at(1);
+ QByteArray eventName = parts.at(2);
+ JsonValue data(parts.at(3));
+ if (eventName != "peerHeartBeat")
+ SDEBUG(_("\nTCF EVENT:") << quoteUnprintableLatin1(response)
+ << data.toString());
+ if (service == "Locator" && eventName == "Hello") {
+ m_services.clear();
+ foreach (const JsonValue &service, data.children())
+ m_services.append(service.data());
+ QTimer::singleShot(0, this, SLOT(startDebugging()));
+ }
+ } else {
+ SDEBUG("UNKNOWN RESPONSE PACKET:"
+ << quoteUnprintableLatin1(response) << parts);
+ }
+}
+
+void TcfEngine::startDebugging()
+{
+ //foreach (const QByteArray &service, m_services) {
+ // postCommand(CB(handleRunControlGetChildren),
+ // service, "getChildren", "\"\"");
+ //}
+
+ postCommand(C("Diagnostics", "getChildren", "\"\""),
+ CB(handleRunControlGetChildren));
+ postCommand(C("Streams", "getChildren", "\"\""));
+ postCommand(C("Expressions", "getChildren", "\"\""));
+ postCommand(C("SysMonitor", "getChildren", "\"\""));
+ //postCommand(C("FileSystem", "getChildren", "\"\""));
+ //postCommand(C("Processes", "getChildren", "\"\""));
+ //postCommand(CB(handleRunControlGetChildren), "LineNumbers", "getChildren");
+ //postCommand(CB(handleRunControlGetChildren), "Symbols", "getChildren");
+ //postCommand(CB(handleRunControlGetChildren), "StackTrace", "getChildren");
+ //postCommand(CB(handleRunControlGetChildren), "Registers", "getChildren");
+ //postCommand(CB(handleRunControlGetChildren), "Memory", "getChildren");
+ //postCommand(CB(handleRunControlGetChildren), "Breakpoints", "getChildren");
+ //postCommand(CB(handleRunControlGetChildren), "RunControl", "getChildren");
+ //postCommand(CB(handleRunControlGetChildren), "Locator", "getChildren");
+
+
+ //postCommand(CB(handleRunControlSuspend),
+ // "RunControl", "suspend", "\"Thread1\"");
+ //postCommand(CB(handleRunControlSuspend),
+ // "RunControl", "getContext", "\"P12318\"");
+
+ //postCommand(C("Locator", "sync"), CB(handleRunControlGetChildren));
+ //postCommand("Locator", "redirect", "ID");
+
+ //postCommand(C("FileSystem", "open", "\"/bin/ls\"", "1", "2", "3"),
+ // CB(handleRunControlGetChildren));
+ postCommand(C("FileSystem", "stat", "\"/bin/ls\""),
+ CB(handleRunControlGetChildren));
+}
+
+void TcfEngine::postCommand(const QByteArray &cmd,
+ TcfCommandCallback callback, const char *callbackName)
+{
+ static int token = 20;
+ ++token;
+
+ //const char marker_eom = -1;
+ //const char marker_eos = -2;
+ //const char marker_null = -3;
+
+ QByteArray ba = "C";
+ ba.append('\0');
+ ba.append(QByteArray::number(token));
+ ba.append('\0');
+ ba.append(cmd);
+ ba.append('\0');
+ ba.append('\3');
+ ba.append('\1');
+
+ TcfCommand tcf;
+ tcf.command = ba;
+ tcf.callback = callback;
+ tcf.callbackName = callbackName;
+ tcf.token = token;
+
+ m_cookieForToken[token] = tcf;
+
+ enqueueCommand(tcf);
+}
+
+// Congestion control does not seem to work that way. Basically it's
+// already too late when we get a flow control packet
+void TcfEngine::enqueueCommand(const TcfCommand &cmd)
+{
+#ifdef USE_CONGESTION_CONTROL
+ // congestion controled
+ if (m_congestion <= 0 && m_sendQueue.isEmpty()) {
+ //SDEBUG("DIRECT SEND" << cmd.toString());
+ sendCommandNow(cmd);
+ } else {
+ SDEBUG("QUEUE " << cmd.toString());
+ m_sendQueue.enqueue(cmd);
+ m_sendTimer.start();
+ }
+#else
+ // synchrounously
+ if (m_inAir == 0)
+ sendCommandNow(cmd);
+ else
+ m_sendQueue.enqueue(cmd);
+#endif
+}
+
+void TcfEngine::handleSendTimer()
+{
+ QTC_ASSERT(!m_sendQueue.isEmpty(), return);
+
+ if (m_congestion > 0) {
+ // not ready...
+ SDEBUG("WAITING FOR CONGESTION TO GO DOWN...");
+ m_sendTimer.start();
+ } else {
+ // go!
+ sendCommandNow(m_sendQueue.dequeue());
+ }
+}
+
+void TcfEngine::sendCommandNow(const TcfCommand &cmd)
+{
+ ++m_inAir;
+ int result = m_socket->write(cmd.command);
+ Q_UNUSED(result)
+ m_socket->flush();
+ showDebuggerInput(LogInput, QString::number(cmd.token) + " " + cmd.toString());
+ SDEBUG("SEND " << cmd.toString()); //<< " " << QString::number(result));
+}
+
+void TcfEngine::acknowledgeResult()
+{
+#if !defined(USE_CONGESTION_CONTROL)
+ QTC_ASSERT(m_inAir == 1, /**/);
+ m_inAir = 0;
+ if (!m_sendQueue.isEmpty())
+ sendCommandNow(m_sendQueue.dequeue());
+#endif
+}
+
+void TcfEngine::handleRunControlSuspend(const JsonValue &data, const QVariant &)
+{
+ SDEBUG("HANDLE RESULT" << data.toString());
+}
+
+void TcfEngine::handleRunControlGetChildren(const JsonValue &data, const QVariant &)
+{
+ SDEBUG("HANDLE RUN CONTROL GET CHILDREN" << data.toString());
+}
+
+void TcfEngine::handleSysMonitorGetChildren(const JsonValue &data, const QVariant &)
+{
+ SDEBUG("HANDLE RUN CONTROL GET CHILDREN" << data.toString());
+}
+
+
+//////////////////////////////////////////////////////////////////////
+//
+// Tooltip specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+static WatchData m_toolTip;
+static QPoint m_toolTipPos;
+static QHash<QString, WatchData> m_toolTipCache;
+
+void TcfEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
+{
+ Q_UNUSED(mousePos)
+ Q_UNUSED(editor)
+ Q_UNUSED(cursorPos)
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Watch specific stuff
+//
+//////////////////////////////////////////////////////////////////////
+
+void TcfEngine::assignValueInDebugger(const QString &expression,
+ const QString &value)
+{
+ XSDEBUG("ASSIGNING: " << expression + '=' + value);
+ updateLocals();
+}
+
+void TcfEngine::updateLocals()
+{
+}
+
+void TcfEngine::updateWatchData(const WatchData &)
+{
+ //qq->watchHandler()->rebuildModel();
+ showStatusMessage(tr("Stopped."), 5000);
+}
+
+void TcfEngine::updateSubItem(const WatchData &data0)
+{
+ Q_UNUSED(data0)
+ QTC_ASSERT(false, return);
+}
+
+void TcfEngine::debugMessage(const QString &msg)
+{
+ showDebuggerOutput(LogDebug, msg);
+}
+
+IDebuggerEngine *createTcfEngine(DebuggerManager *manager)
+{
+ return new TcfEngine(manager);
+}
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 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 DEBUGGER_TCFENGINE_H
+#define DEBUGGER_TCFENGINE_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QObject>
+#include <QtCore/QPoint>
+#include <QtCore/QProcess>
+#include <QtCore/QQueue>
+#include <QtCore/QSet>
+#include <QtCore/QTimer>
+#include <QtCore/QVariant>
+
+#include <QtNetwork/QAbstractSocket>
+
+QT_BEGIN_NAMESPACE
+class QTcpSocket;
+QT_END_NAMESPACE
+
+#include "idebuggerengine.h"
+#include "debuggermanager.h"
+#include "json.h"
+
+namespace Debugger {
+namespace Internal {
+
+class ScriptAgent;
+class WatchData;
+
+class TcfEngine : public IDebuggerEngine
+{
+ Q_OBJECT
+
+public:
+ explicit TcfEngine(DebuggerManager *parent);
+ ~TcfEngine();
+
+private:
+ // IDebuggerEngine implementation
+ void executeStep();
+ void executeStepOut();
+ void executeNext();
+ void executeStepI();
+ void executeNextI();
+
+ void shutdown();
+ void setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos);
+ void startDebugger(const DebuggerStartParametersPtr &sp);
+ void exitDebugger();
+
+ void continueInferior();
+ Q_SLOT void runInferior();
+ void interruptInferior();
+
+ void executeRunToLine(const QString &fileName, int lineNumber);
+ void executeRunToFunction(const QString &functionName);
+ void executeJumpToLine(const QString &fileName, int lineNumber);
+
+ void activateFrame(int index);
+ void selectThread(int index);
+
+ void attemptBreakpointSynchronization();
+
+ void assignValueInDebugger(const QString &expr, const QString &value);
+ void executeDebuggerCommand(const QString & command);
+
+ void loadSymbols(const QString &moduleName);
+ void loadAllSymbols();
+ void requestModuleSymbols(const QString &moduleName);
+ void reloadModules();
+ void reloadRegisters() {}
+ void reloadSourceFiles() {}
+ void reloadFullStack() {}
+
+ bool supportsThreads() const { return true; }
+ void maybeBreakNow(bool byFunction);
+ void updateWatchData(const WatchData &data);
+ void updateLocals();
+ void updateSubItem(const WatchData &data);
+
+ Q_SLOT void socketConnected();
+ Q_SLOT void socketDisconnected();
+ Q_SLOT void socketError(QAbstractSocket::SocketError);
+ Q_SLOT void socketReadyRead();
+
+ void handleResponse(const QByteArray &ba);
+ void handleRunControlSuspend(const JsonValue &response, const QVariant &);
+ void handleRunControlGetChildren(const JsonValue &response, const QVariant &);
+ void handleSysMonitorGetChildren(const JsonValue &response, const QVariant &);
+
+private:
+ Q_SLOT void startDebugging();
+
+ typedef void (TcfEngine::*TcfCommandCallback)
+ (const JsonValue &record, const QVariant &cookie);
+
+ struct TcfCommand
+ {
+ TcfCommand() : flags(0), token(-1), callback(0), callbackName(0) {}
+
+ QString toString() const;
+
+ int flags;
+ int token;
+ TcfCommandCallback callback;
+ const char *callbackName;
+ QByteArray command;
+ QVariant cookie;
+ };
+
+ void postCommand(const QByteArray &cmd,
+ TcfCommandCallback callback = 0, const char *callbackName = 0);
+ void sendCommandNow(const TcfCommand &command);
+ void debugMessage(const QString &msg);
+
+ QHash<int, TcfCommand> m_cookieForToken;
+
+ QQueue<TcfCommand> m_sendQueue;
+
+ // timer based congestion control. does not seem to work well.
+ void enqueueCommand(const TcfCommand &command);
+ Q_SLOT void handleSendTimer();
+ int m_congestion;
+ QTimer m_sendTimer;
+
+ // synchrounous communication
+ void acknowledgeResult();
+ int m_inAir;
+
+ QTcpSocket *m_socket;
+ QByteArray m_inbuffer;
+ QList<QByteArray> m_services;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // DEBUGGER_TCFENGINE_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "threadshandler.h"
+
+namespace Debugger {
+namespace Internal {
+
+////////////////////////////////////////////////////////////////////////
+//
+// ThreadsHandler
+//
+///////////////////////////////////////////////////////////////////////
+
+ThreadData::ThreadData(int threadId)
+{
+ notifyRunning();
+ id = threadId;
+}
+
+void ThreadData::notifyRunning()
+{
+ address = 0;
+ function.clear();
+ fileName.clear();
+ frameLevel = -1;
+ state.clear();
+ lineNumber = -1;
+}
+
+////////////////////////////////////////////////////////////////////////
+//
+// ThreadsHandler
+//
+///////////////////////////////////////////////////////////////////////
+
+ThreadsHandler::ThreadsHandler(QObject *parent) :
+ QAbstractTableModel(parent),
+ m_currentIndex(0),
+ m_positionIcon(QLatin1String(":/debugger/images/location_16.png")),
+ m_emptyIcon(QLatin1String(":/debugger/images/debugger_empty_14.png"))
+{
+}
+
+int ThreadsHandler::rowCount(const QModelIndex &parent) const
+{
+ // Since the stack is not a tree, row count is 0 for any valid parent
+ return parent.isValid() ? 0 : m_threads.size();
+}
+
+int ThreadsHandler::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : int(ThreadData::ColumnCount);
+}
+
+QVariant ThreadsHandler::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid())
+ return QVariant();
+ const int row = index.row();
+ if (row >= m_threads.size())
+ return QVariant();
+ const ThreadData &thread = m_threads.at(row);
+
+ if (role == Qt::DisplayRole) {
+ switch (index.column()) {
+ case ThreadData::IdColumn:
+ return thread.id;
+ case ThreadData::FunctionColumn:
+ return thread.function;
+ case ThreadData::FileColumn:
+ return thread.fileName;
+ case ThreadData::LineColumn:
+ return thread.lineNumber >= 0 ? QString::number(thread.lineNumber) : QString();
+ case ThreadData::AddressColumn:
+ return thread.address > 0 ? QLatin1String("0x") + QString::number(thread.address, 16) : QString();
+ case ThreadData::CoreColumn:
+ return thread.core;
+ case ThreadData::StateColumn:
+ return thread.state;
+ }
+ } else if (role == Qt::ToolTipRole) {
+ if (thread.address == 0)
+ return tr("Thread: %1").arg(thread.id);
+ // Stopped
+ if (thread.fileName.isEmpty())
+ return tr("Thread: %1 at %2 (0x%3)").arg(thread.id).arg(thread.function).arg(thread.address, 0, 16);
+ return tr("Thread: %1 at %2, %3:%4 (0x%5)").
+ arg(thread.id).arg(thread.function, thread.fileName).arg(thread.lineNumber).arg(thread.address, 0, 16);
+ } else if (role == Qt::DecorationRole && index.column() == 0) {
+ // Return icon that indicates whether this is the active stack frame
+ return (index.row() == m_currentIndex) ? m_positionIcon : m_emptyIcon;
+ }
+
+ return QVariant();
+}
+
+QVariant ThreadsHandler::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
+ return QVariant();
+ switch (section) {
+ case ThreadData::IdColumn:
+ return tr("Thread ID");
+ case ThreadData::FunctionColumn:
+ return tr("Function");
+ case ThreadData::FileColumn:
+ return tr("File");
+ case ThreadData::LineColumn:
+ return tr("Line");
+ case ThreadData::AddressColumn:
+ return tr("Address");
+ case ThreadData::CoreColumn:
+ return tr("Core");
+ case ThreadData::StateColumn:
+ return tr("State");
+ }
+ return QVariant();
+}
+
+int ThreadsHandler::currentThreadId() const
+{
+ if (m_currentIndex < 0 || m_currentIndex >= m_threads.size())
+ return -1;
+ return m_threads[m_currentIndex].id;
+}
+
+void ThreadsHandler::setCurrentThread(int index)
+{
+ if (index == m_currentIndex)
+ return;
+
+ // Emit changed for previous frame
+ QModelIndex i = ThreadsHandler::index(m_currentIndex, 0);
+ emit dataChanged(i, i);
+
+ m_currentIndex = index;
+
+ // Emit changed for new frame
+ i = ThreadsHandler::index(m_currentIndex, 0);
+ emit dataChanged(i, i);
+}
+
+void ThreadsHandler::setThreads(const QList<ThreadData> &threads)
+{
+ m_threads = threads;
+ if (m_currentIndex >= m_threads.size())
+ m_currentIndex = m_threads.size() - 1;
+ reset();
+}
+
+QList<ThreadData> ThreadsHandler::threads() const
+{
+ return m_threads;
+}
+
+void ThreadsHandler::removeAll()
+{
+ m_threads.clear();
+ m_currentIndex = 0;
+ reset();
+}
+
+void ThreadsHandler::notifyRunning()
+{
+ // Threads stopped (that is, address != 0 showing)?
+ if (m_threads.empty())
+ return;
+ if (m_threads.front().address == 0)
+ return;
+ const QList<ThreadData>::iterator end = m_threads.end();
+ for (QList<ThreadData>::iterator it = m_threads.begin(); it != end; ++it)
+ it->notifyRunning();
+ emit dataChanged(index(0, 1),
+ index(m_threads.size() - 1, ThreadData::ColumnCount - 1));
+}
+
+} // namespace Internal
+} // namespace Debugger
--- /dev/null
+/**************************************************************************
+**
+** 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 THREADSHANDLER_H
+#define THREADSHANDLER_H
+
+#include <QtCore/QAbstractTableModel>
+#include <QtCore/QList>
+
+#include <QtGui/QIcon>
+
+
+namespace Debugger {
+namespace Internal {
+
+////////////////////////////////////////////////////////////////////////
+//
+// ThreadData
+//
+////////////////////////////////////////////////////////////////////////
+
+/*! A structure containing information about a single thread */
+struct ThreadData
+{
+ ThreadData(int threadId = 0);
+
+ enum {
+ IdColumn,
+ AddressColumn,
+ FunctionColumn,
+ FileColumn,
+ LineColumn,
+ StateColumn,
+ CoreColumn,
+ ColumnCount = CoreColumn
+ };
+
+ // Permanent data.
+ int id;
+ QString targetId;
+ QString core;
+
+ // State information when stopped
+ void notifyRunning(); // Clear state information
+
+ int frameLevel;
+ quint64 address;
+ QString function;
+ QString fileName;
+ QString state;
+ int lineNumber;
+};
+
+
+////////////////////////////////////////////////////////////////////////
+//
+// ThreadsHandler
+//
+////////////////////////////////////////////////////////////////////////
+
+/*! A model to represent the running threads in a QTreeView or ComboBox */
+class ThreadsHandler : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+ ThreadsHandler(QObject *parent = 0);
+
+ int currentThreadId() const;
+ void setCurrentThread(int index);
+ void selectThread(int index);
+ void setThreads(const QList<ThreadData> &threads);
+ void removeAll();
+ QList<ThreadData> threads() const;
+ QAbstractItemModel *threadsModel() { return this; }
+
+ // Clear out all frame information
+ void notifyRunning();
+
+private:
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+private:
+ QList<ThreadData> m_threads;
+ int m_currentIndex;
+ const QIcon m_positionIcon;
+ const QIcon m_emptyIcon;
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // THREADSHANDLER_H
m_root->parent = 0;
switch (m_type) {
+ case ReturnWatch:
+ m_root->iname = "return";
+ m_root->name = WatchHandler::tr("Return Value");
+ break;
case LocalsWatch:
m_root->iname = "local";
m_root->name = WatchHandler::tr("Locals");
return v;
}
+// Get a pointer address from pointer values reported by the debugger.
+// Fix CDB formatting of pointers "0x00000000`000003fd class foo *",
+// check gdb formatting of characters.
+static inline quint64 pointerValue(QString data)
+{
+ if (data.isEmpty() || !data.startsWith(QLatin1String("0x")))
+ return 0;
+ data.remove(0, 2);
+ const int blankPos = data.indexOf(QLatin1Char(' '));
+ if (blankPos != -1)
+ data.truncate(blankPos);
+ data.remove(QLatin1Char('`'));
+ bool ok;
+ const quint64 address = data.toULongLong(&ok, 16);
+ return ok ? address : quint64(0);
+}
+
QVariant WatchModel::data(const QModelIndex &idx, int role) const
{
const WatchItem *item = watchItem(idx);
case IndividualFormatRole:
return m_handler->m_individualFormats.value(data.addr, -1);
- case AddressRole: {
- if (!data.addr.isEmpty())
- return data.addr;
- bool ok;
- (void) data.value.toULongLong(&ok, 0);
- if (ok)
- return data.value;
- return QVariant();
- }
+ case AddressRole:
+ if (!data.addr.isEmpty()) {
+ bool ok;
+ const quint64 address = data.addr.toULongLong(&ok, 16);
+ if (ok)
+ return QVariant(address);
+ }
+ return QVariant(quint64(0));
+
+ case RawValueRole:
+ return data.value;
+ case PointerValue:
+ if (isPointerType(data.type))
+ return pointerValue(data.value);
+ return QVariant(quint64(0));
default:
break;
}
m_expandPointers = true;
m_inChange = false;
+ m_return = new WatchModel(this, ReturnWatch);
m_locals = new WatchModel(this, LocalsWatch);
m_watchers = new WatchModel(this, WatchersWatch);
m_tooltips = new WatchModel(this, TooltipsWatch);
void WatchHandler::beginCycle()
{
++generationCounter;
+ m_return->beginCycle();
m_locals->beginCycle();
m_watchers->beginCycle();
m_tooltips->beginCycle();
void WatchHandler::endCycle()
{
+ m_return->endCycle();
m_locals->endCycle();
m_watchers->endCycle();
m_tooltips->endCycle();
void WatchHandler::cleanup()
{
m_expandedINames.clear();
+ m_return->reinitialize();
m_locals->reinitialize();
m_tooltips->reinitialize();
+ m_return->m_fetchTriggered.clear();
m_locals->m_fetchTriggered.clear();
m_watchers->m_fetchTriggered.clear();
m_tooltips->m_fetchTriggered.clear();
void WatchHandler::emitAllChanged()
{
+ m_return->emitAllChanged();
m_locals->emitAllChanged();
m_watchers->emitAllChanged();
m_tooltips->emitAllChanged();
WatchModel *WatchHandler::model(WatchType type) const
{
switch (type) {
+ case ReturnWatch: return m_return;
case LocalsWatch: return m_locals;
case WatchersWatch: return m_watchers;
case TooltipsWatch: return m_tooltips;
WatchModel *WatchHandler::modelForIName(const QByteArray &iname) const
{
+ if (iname.startsWith("return"))
+ return m_return;
if (iname.startsWith("local"))
return m_locals;
if (iname.startsWith("tooltip"))
void WatchHandler::setFormat(const QString &type, int format)
{
- m_typeFormats[type] = format;
+ if (format == -1)
+ m_typeFormats.remove(type);
+ else
+ m_typeFormats[type] = format;
saveTypeFormats();
+ m_return->emitDataChanged(1);
m_locals->emitDataChanged(1);
m_watchers->emitDataChanged(1);
m_tooltips->emitDataChanged(1);
class WatchItem;
class WatchHandler;
-enum WatchType { LocalsWatch, WatchersWatch, TooltipsWatch };
+enum WatchType { ReturnWatch, LocalsWatch, WatchersWatch, TooltipsWatch };
enum WatchRoles
{
TypeFormatListRole,
TypeFormatRole, // Used to communicate alternative formats to the view.
IndividualFormatRole,
- AddressRole, // Some memory address related to the object.
+ AddressRole, // Memory address of variable as quint64.
+ RawValueRole, // Unformatted value as string.
+ PointerValue // Pointer value (address) as quint64.
};
enum IntegerFormat
// Items expanded in the Locals & Watchers view.
QSet<QByteArray> m_expandedINames;
+ WatchModel *m_return;
WatchModel *m_locals;
WatchModel *m_watchers;
WatchModel *m_tooltips;
const CPlusPlus::Document::Ptr doc = docIt.value();
// Look at symbol at line and find its function. Either it is the
// function itself or some expression/variable.
- const CPlusPlus::Symbol *symbolAtLine = doc->findSymbolAt(line, 0);
+ const CPlusPlus::Symbol *symbolAtLine = doc->lastVisibleSymbolAt(line, 0);
if (!symbolAtLine)
return 4;
// First figure out the function to do a safety name check
#include "watchwindow.h"
#include "watchhandler.h"
+#include "breakpoint.h"
+#include "breakhandler.h"
#include "debuggeractions.h"
#include "debuggeragents.h"
#include "debuggerdialogs.h"
//QTreeView::dropEvent(ev);
}
+static inline void toggleWatchPoint(DebuggerManager *manager, quint64 address)
+{
+ const QByteArray addressBA = QByteArray("0x") + QByteArray::number(address, 16);
+ const int index = manager->breakHandler()->findWatchPointIndexByAddress(addressBA);
+ if (index == -1) {
+ BreakpointData *data = new BreakpointData;
+ data->type = BreakpointData::WatchpointType;
+ data->address = addressBA;
+ manager->breakHandler()->appendBreakpoint(data);
+ } else {
+ manager->breakHandler()->removeBreakpoint(index);
+ }
+ manager->attemptBreakpointSynchronization();
+}
+
void WatchWindow::contextMenuEvent(QContextMenuEvent *ev)
{
const QModelIndex idx = indexAt(ev->pos());
const QModelIndex mi0 = idx.sibling(idx.row(), 0);
const QModelIndex mi1 = idx.sibling(idx.row(), 1);
const QModelIndex mi2 = idx.sibling(idx.row(), 2);
- const QString addr = model()->data(mi0, AddressRole).toString();
+ const quint64 address = model()->data(mi0, AddressRole).toULongLong();
+ const quint64 pointerValue = model()->data(mi0, PointerValue).toULongLong();
const QString exp = model()->data(mi0, ExpressionRole).toString();
const QString type = model()->data(mi2).toString();
const QStringList alternativeFormats =
model()->data(mi0, TypeFormatListRole).toStringList();
const int typeFormat =
- qMax(int(DecimalFormat), model()->data(mi0, TypeFormatRole).toInt());
+ model()->data(mi0, TypeFormatRole).toInt();
const int individualFormat =
model()->data(mi0, IndividualFormatRole).toInt();
const int effectiveIndividualFormat =
QMenu typeFormatMenu;
QList<QAction *> typeFormatActions;
+ QAction *clearTypeFormatAction = 0;
if (idx.isValid()) {
typeFormatMenu.setTitle(
tr("Change Format for Type \"%1\"").arg(type));
if (alternativeFormats.isEmpty()) {
typeFormatMenu.setEnabled(false);
} else {
+ clearTypeFormatAction = typeFormatMenu.addAction(tr("Clear"));
+ clearTypeFormatAction->setEnabled(typeFormat != -1);
+ clearTypeFormatAction->setCheckable(true);
+ clearTypeFormatAction->setChecked(typeFormat == -1);
+ typeFormatMenu.addSeparator();
for (int i = 0; i != alternativeFormats.size(); ++i) {
const QString format = alternativeFormats.at(i);
QAction *act = new QAction(format, &typeFormatMenu);
QMenu individualFormatMenu;
QList<QAction *> individualFormatActions;
QAction *clearIndividualFormatAction = 0;
- if (idx.isValid() && !addr.isEmpty()) {
+ if (idx.isValid() && address) {
individualFormatMenu.setTitle(
- tr("Change Format for Object at %1").arg(addr));
+ tr("Change Format for Object at 0x%1").arg(address, 0, 16));
if (alternativeFormats.isEmpty()) {
individualFormatMenu.setEnabled(false);
} else {
clearIndividualFormatAction = individualFormatMenu.addAction(tr("Clear"));
clearIndividualFormatAction->setEnabled(individualFormat != -1);
+ clearIndividualFormatAction->setCheckable(true);
+ clearIndividualFormatAction->setChecked(individualFormat == -1);
individualFormatMenu.addSeparator();
for (int i = 0; i != alternativeFormats.size(); ++i) {
const QString format = alternativeFormats.at(i);
QAction *actSelectWidgetToWatch = menu.addAction(tr("Select Widget to Watch"));
actSelectWidgetToWatch->setEnabled(canHandleWatches);
- const QString address = model()->data(mi0, AddressRole).toString();
- QAction *actWatchKnownMemory = 0;
- QAction *actWatchUnknownMemory = new QAction(tr("Open Memory Editor..."), &menu);
+ QAction *actOpenMemoryEditAtVariableAddress = 0;
+ QAction *actOpenMemoryEditAtPointerValue = 0;
+ QAction *actOpenMemoryEditor =
+ new QAction(tr("Open Memory Editor..."), &menu);
const bool canShowMemory = engineCapabilities & ShowMemoryCapability;
- actWatchUnknownMemory->setEnabled(actionsEnabled && canShowMemory);
+ actOpenMemoryEditor->setEnabled(actionsEnabled && canShowMemory);
+
+ // Offer to open address pointed to or variable address.
+ const bool createPointerActions = pointerValue && pointerValue != address;
- if (canShowMemory && !address.isEmpty())
- actWatchKnownMemory =
- new QAction(tr("Open Memory Editor at %1").arg(address), &menu);
+ if (canShowMemory && address)
+ actOpenMemoryEditAtVariableAddress =
+ new QAction(tr("Open Memory Editor at Object's Address (0x%1)").arg(address, 0, 16), &menu);
+ if (createPointerActions)
+ actOpenMemoryEditAtPointerValue =
+ new QAction(tr("Open Memory Editor at Referenced Address (0x%1)").arg(pointerValue, 0, 16), &menu);
menu.addSeparator();
+ QAction *actSetWatchPointAtVariableAddress = 0;
+ QAction *actSetWatchPointAtPointerValue= 0;
+ const bool canSetWatchpoint = engineCapabilities & WatchpointCapability;
+ if (canSetWatchpoint && address) {
+ actSetWatchPointAtVariableAddress =
+ new QAction(tr("Break on Changes at Object's Address (0x%1)").arg(address, 0, 16), &menu);
+ actSetWatchPointAtVariableAddress->setCheckable(true);
+ actSetWatchPointAtVariableAddress->setChecked(m_manager->breakHandler()->watchPointAt(address));
+ if (createPointerActions) {
+ actSetWatchPointAtPointerValue =
+ new QAction(tr("Break on Changes at Referenced Address (0x%1)").arg(pointerValue, 0, 16), &menu);
+ actSetWatchPointAtPointerValue->setCheckable(true);
+ actSetWatchPointAtPointerValue->setChecked(m_manager->breakHandler()->watchPointAt(pointerValue));
+ }
+ } else {
+ actSetWatchPointAtVariableAddress =
+ new QAction(tr("Break on Changing Contents"), &menu);
+ actSetWatchPointAtVariableAddress->setEnabled(false);
+ }
+
QAction *actWatchOrRemove;
if (m_type == LocalsType) {
actWatchOrRemove = theDebuggerAction(WatchExpression)->updatedAction(exp);
menu.addAction(actSelectWidgetToWatch);
menu.addMenu(&typeFormatMenu);
menu.addMenu(&individualFormatMenu);
- if (actWatchKnownMemory)
- menu.addAction(actWatchKnownMemory);
- menu.addAction(actWatchUnknownMemory);
+ if (actOpenMemoryEditAtVariableAddress)
+ menu.addAction(actOpenMemoryEditAtVariableAddress);
+ if (actOpenMemoryEditAtPointerValue)
+ menu.addAction(actOpenMemoryEditAtPointerValue);
+ menu.addAction(actOpenMemoryEditor);
+ menu.addAction(actSetWatchPointAtVariableAddress);
+ if (actSetWatchPointAtPointerValue)
+ menu.addAction(actSetWatchPointAtPointerValue);
menu.addSeparator();
menu.addAction(theDebuggerAction(RecheckDebuggingHelpers));
menu.addAction(theDebuggerAction(SettingsDialog));
QAction *act = menu.exec(ev->globalPos());
+ if (act == 0)
+ return;
if (act == actAdjustColumnWidths) {
resizeColumnsToContents();
} else if (act == actInsertNewWatchItem) {
theDebuggerAction(WatchExpression)
->trigger(WatchHandler::watcherEditPlaceHolder());
- } else if (actWatchKnownMemory != 0 && act == actWatchKnownMemory) {
+ } else if (act == actOpenMemoryEditAtVariableAddress) {
(void) new MemoryViewAgent(m_manager, address);
- } else if (actWatchUnknownMemory != 0 && act == actWatchUnknownMemory) {
+ } else if (act == actOpenMemoryEditAtPointerValue) {
+ (void) new MemoryViewAgent(m_manager, pointerValue);
+ } else if (act == actOpenMemoryEditor) {
AddressDialog dialog;
if (dialog.exec() == QDialog::Accepted) {
(void) new MemoryViewAgent(m_manager, dialog.address());
}
+ } else if (act == actSetWatchPointAtVariableAddress) {
+ toggleWatchPoint(m_manager, address);
+ } else if (act == actSetWatchPointAtPointerValue) {
+ toggleWatchPoint(m_manager, pointerValue);
} else if (act == actSelectWidgetToWatch) {
grabMouse(Qt::CrossCursor);
m_grabbing = true;
} else if (act == actClearCodeModelSnapshot) {
m_manager->clearCppCodeModelSnapshot();
- } else if (clearIndividualFormatAction && act == clearIndividualFormatAction) {
+ } else if (act == clearTypeFormatAction) {
+ model()->setData(mi1, -1, TypeFormatRole);
+ } else if (act == clearIndividualFormatAction) {
model()->setData(mi1, -1, IndividualFormatRole);
} else {
for (int i = 0; i != typeFormatActions.size(); ++i) {
Q_OBJECT
public:
- enum Type { LocalsType, TooltipType, WatchersType };
+ enum Type { ReturnType, LocalsType, TooltipType, WatchersType };
WatchWindow(Type type, DebuggerManager *manager, QWidget *parent = 0);
void setType(Type type) { m_type = type; }
-<plugin name="Designer" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Designer" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Qt Designer integration.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
<!-- For compiling with CPP support enabled -->
- <dependency name="CppEditor" version="1.3.84"/>
+ <dependency name="CppEditor" version="2.0.80"/>
</dependencyList>
</plugin>
connect(&(d->m_file), SIGNAL(reload(QString)), this, SLOT(slotOpen(QString)));
// Force update of open editors model.
connect(&(d->m_file), SIGNAL(saved()), this, SIGNAL(changed()));
+ connect(&(d->m_file), SIGNAL(changed()), this, SIGNAL(changed()));
}
FormWindowEditor::~FormWindowEditor()
return true;
}
+void FormWindowFile::rename(const QString &newName)
+{
+ m_formWindow->setFileName(newName);
+ QFileInfo fi(newName);
+ m_fileName = fi.absoluteFilePath();
+ emit setDisplayName(fi.fileName());
+ emit changed();
+}
+
QString FormWindowFile::fileName() const
{
return m_fileName;
virtual QString defaultPath() const;
virtual QString suggestedFileName() const;
virtual QString mimeType() const;
+ virtual void rename(const QString &newName);
// Internal
void setSuggestedFileName(const QString &fileName);
#include <cplusplus/Literals.h>
#include <cplusplus/Scope.h>
#include <cplusplus/Control.h>
-#include <cplusplus/LookupContext.h>
+#include <cplusplus/TranslationUnit.h>
+#include <cplusplus/DeprecatedLookupContext.h>
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <extensionsystem/pluginmanager.h>
return 0;
}
-static const Function *findDeclaration(const Class *cl, const QString &functionName)
+static Function *findDeclaration(const Class *cl, const QString &functionName)
{
const QString funName = QString::fromUtf8(QMetaObject::normalizedSignature(functionName.toUtf8()));
const unsigned mCount = cl->memberCount();
// we are only interested in declarations of methods
const Overview overview;
for (unsigned j = 0; j < mCount; j++) { // go through all members
- if (const Declaration *decl = cl->memberAt(j)->asDeclaration())
- if (const Function *fun = decl->type()->asFunctionType()) {
+ if (Declaration *decl = cl->memberAt(j)->asDeclaration())
+ if (Function *fun = decl->type()->asFunctionType()) {
// Format signature
QString memberFunction = overview.prettyName(fun->name());
memberFunction += QLatin1Char('(');
return 0;
}
-// TODO: remove me, see below
-static bool isCompatible(const Name *name, const Name *otherName)
-{
- if (const NameId *nameId = name->asNameId()) {
- if (const TemplateNameId *otherTemplId = otherName->asTemplateNameId())
- return nameId->identifier()->isEqualTo(otherTemplId->identifier());
- } else if (const TemplateNameId *templId = name->asTemplateNameId()) {
- if (const NameId *otherNameId = otherName->asNameId())
- return templId->identifier()->isEqualTo(otherNameId->identifier());
- }
-
- return name->isEqualTo(otherName);
-}
-
-// TODO: remove me, see below
-static bool isCompatible(const Function *definition, const Symbol *declaration, const QualifiedNameId *declarationName)
-{
- Function *declTy = declaration->type()->asFunctionType();
- if (! declTy)
- return false;
-
- const Name *definitionName = definition->name();
- if (const QualifiedNameId *q = definitionName->asQualifiedNameId()) {
- if (! isCompatible(q->unqualifiedNameId(), declaration->name()))
- return false;
- else if (q->nameCount() > declarationName->nameCount())
- return false;
- else if (declTy->argumentCount() != definition->argumentCount())
- return false;
- else if (declTy->isConst() != definition->isConst())
- return false;
- else if (declTy->isVolatile() != definition->isVolatile())
- return false;
-
- for (unsigned i = 0; i < definition->argumentCount(); ++i) {
- Symbol *arg = definition->argumentAt(i);
- Symbol *otherArg = declTy->argumentAt(i);
- if (! arg->type().isEqualTo(otherArg->type()))
- return false;
- }
-
- for (unsigned i = 0; i != q->nameCount(); ++i) {
- const Name *n = q->nameAt(q->nameCount() - i - 1);
- const Name *m = declarationName->nameAt(declarationName->nameCount() - i - 1);
- if (! isCompatible(n, m))
- return false;
- }
- return true;
- } else {
- // ### TODO: implement isCompatible for unqualified name ids.
- }
- return false;
-}
-
// TODO: remove me, this is taken from cppeditor.cpp. Find some common place for this method
-static Document::Ptr findDefinition(const Function *functionDeclaration, int *line)
+static Document::Ptr findDefinition(Function *functionDeclaration, int *line)
{
- CppTools::CppModelManagerInterface *cppModelManager = cppModelManagerInstance();
- if (!cppModelManager)
- return Document::Ptr();
+ if (CppTools::CppModelManagerInterface *cppModelManager = cppModelManagerInstance()) {
+ const Snapshot snapshot = cppModelManager->snapshot();
- QVector<const Name *> qualifiedName;
- Scope *scope = functionDeclaration->scope();
- for (; scope; scope = scope->enclosingScope()) {
- if (scope->isClassScope() || scope->isNamespaceScope()) {
- if (scope->owner() && scope->owner()->name()) {
- const Name *scopeOwnerName = scope->owner()->name();
- if (const QualifiedNameId *q = scopeOwnerName->asQualifiedNameId()) {
- for (unsigned i = 0; i < q->nameCount(); ++i) {
- qualifiedName.prepend(q->nameAt(i));
+ if (Symbol *def = snapshot.findMatchingDefinition(functionDeclaration)) {
+ if (line)
+ *line = def->line();
-}
- } else {
- qualifiedName.prepend(scopeOwnerName);
- }
- }
+ return snapshot.document(QString::fromUtf8(def->fileName(), def->fileNameLength()));
}
}
- qualifiedName.append(functionDeclaration->name());
-
- Control control;
- const QualifiedNameId *q = control.qualifiedNameId(&qualifiedName[0], qualifiedName.size());
- LookupContext context(&control);
- const Snapshot documents = cppModelManager->snapshot();
- foreach (Document::Ptr doc, documents) {
- QList<Scope *> visibleScopes;
- visibleScopes.append(doc->globalSymbols());
- visibleScopes = context.expand(visibleScopes);
- foreach (Scope *visibleScope, visibleScopes) {
- Symbol *symbol = 0;
- if (const NameId *nameId = q->unqualifiedNameId()->asNameId())
- symbol = visibleScope->lookat(nameId->identifier());
- else if (const DestructorNameId *dtorId = q->unqualifiedNameId()->asDestructorNameId())
- symbol = visibleScope->lookat(dtorId->identifier());
- else if (const TemplateNameId *templNameId = q->unqualifiedNameId()->asTemplateNameId())
- symbol = visibleScope->lookat(templNameId->identifier());
- else if (const OperatorNameId *opId = q->unqualifiedNameId()->asOperatorNameId())
- symbol = visibleScope->lookat(opId->kind());
- // ### cast operators
- for (; symbol; symbol = symbol->next()) {
- if (! symbol->isFunction())
- continue;
- else if (! isCompatible(symbol->asFunction(), functionDeclaration, q))
- continue;
- *line = symbol->line(); // TODO: shift the line so that we are inside a function. Maybe just find the nearest '{'?
- return doc;
- }
- }
- }
return Document::Ptr();
-
}
static bool isEndingQuote(const QString &contents, int quoteIndex)
return qobject_cast<ITextEditable *>(TextEditor::BaseTextEditor::openEditorAt(fileName, line, column));
}
-static void addDeclaration(const QString &docFileName, const Class *cl, const QString &functionName)
+static void addDeclaration(Document::Ptr doc, const Class *cl, const QString &functionName)
{
+ const QString docFileName = doc->fileName();
+ TranslationUnit *unit = doc->translationUnit();
QString declaration = QLatin1String("void ");
declaration += functionName;
declaration += QLatin1String(";\n");
const int column = fun->column();
if (ITextEditable *editable = editableAt(docFileName, line, column)) {
unsigned dl, dc; // this position is the beginning of return value: "^void foo(...)"
- decl->getStartPosition(&dl, &dc);
+ unit->getPosition(decl->startOffset(), &dl, &dc);
dc--; // if the first character in line is 'v' coming from "void" getStartPosition returns 1, not 0, so we always decrement it.
editable->gotoLine(dl, dc);
editable->position(ITextEditor::StartOfLine);
int line = 0;
Document::Ptr sourceDoc;
- if (const Function *fun = findDeclaration(cl, functionName)) {
+ if (Function *fun = findDeclaration(cl, functionName)) {
sourceDoc = findDefinition(fun, &line);
if (!sourceDoc) {
// add function definition to cpp file
}
} else {
// add function declaration to cl
- addDeclaration(doc->fileName(), cl, functionNameWithParameterNames);
+ addDeclaration(doc, cl, functionNameWithParameterNames);
// add function definition to cpp file
sourceDoc = addDefinition(docTable, doc->fileName(), className, functionNameWithParameterNames, &line);
-<plugin name="FakeVim" version="1.3.84" compatVersion="1.3.84">
+<plugin name="FakeVim" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>VI-style keyboard navigation.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="CppEditor" version="1.3.84"/><!-- Plugin adds items to the editor's context menu -->
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="CppEditor" version="2.0.80"/><!-- Plugin adds items to the editor's context menu -->
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
item->setSettingsKey(group, _("IsKeyword"));
instance->insertItem(ConfigIsKeyword, item, _("iskeyword"), _("isk"));
+ // Invented here.
item = new SavedAction(instance);
- item->setText(QCoreApplication::translate("FakeVim::Internal",
- "FakeVim properties..."));
- instance->insertItem(SettingsDialog, item);
+ item->setDefaultValue(false);
+ item->setValue(false);
+ item->setSettingsKey(group, _("ShowMarks"));
+ item->setCheckable(true);
+ instance->insertItem(ConfigShowMarks, item, _("showmarks"), _("sm"));
return instance;
}
ConfigIsKeyword,
// other actions
- SettingsDialog,
+ ConfigShowMarks,
};
class FakeVimSettings : public QObject
// m_tc.position() (== position()). The character below position() is not included
// if the last movement command was exclusive (MoveExclusive).
// The value of m_tc.anchor() is not used.
+//
#include "fakevimhandler.h"
#include "fakevimsyntax.h"
#include <QtCore/QProcess>
#include <QtCore/QRegExp>
#include <QtCore/QTextStream>
+#include <QtCore/QTimer>
#include <QtCore/QtAlgorithms>
#include <QtCore/QStack>
#define EDITOR(s) (m_textedit ? m_textedit->s : m_plaintextedit->s)
const int ParagraphSeparator = 0x00002029;
+typedef QLatin1String _;
using namespace Qt;
+/*! A \e Mode represents one of the basic modes of operation of FakeVim.
+*/
enum Mode
{
InsertMode,
+ ReplaceMode,
CommandMode,
ExMode,
- SearchForwardMode,
- SearchBackwardMode,
};
+/*! A \e SubMode is used for things that require one more data item
+ and are 'nested' behind a \l Mode.
+*/
enum SubMode
{
NoSubMode,
- ChangeSubMode, // used for c
- DeleteSubMode, // used for d
- FilterSubMode, // used for !
- IndentSubMode, // used for =
- RegisterSubMode, // used for "
- ReplaceSubMode, // used for R and r
- ShiftLeftSubMode, // used for <
- ShiftRightSubMode, // used for >
- TransformSubMode, // used for ~/gu/gU
- WindowSubMode, // used for Ctrl-w
- YankSubMode, // used for y
- ZSubMode, // used for z
- CapitalZSubMode // used for Z
+ ChangeSubMode, // Used for c
+ DeleteSubMode, // Used for d
+ FilterSubMode, // Used for !
+ IndentSubMode, // Used for =
+ RegisterSubMode, // Used for "
+ ShiftLeftSubMode, // Used for <
+ ShiftRightSubMode, // Used for >
+ TransformSubMode, // Used for ~/gu/gU
+ WindowSubMode, // Used for Ctrl-w
+ YankSubMode, // Used for y
+ ZSubMode, // Used for z
+ CapitalZSubMode // Used for Z
};
+/*! A \e SubSubMode is used for things that require one more data item
+ and are 'nested' behind a \l SubMode.
+*/
enum SubSubMode
{
- // typically used for things that require one more data item
- // and are 'nested' behind a mode
NoSubSubMode,
- FtSubSubMode, // used for f, F, t, T
- MarkSubSubMode, // used for m
- BackTickSubSubMode, // used for `
- TickSubSubMode, // used for '
- InvertCaseSubSubMode, // used for ~
- DownCaseSubSubMode, // used for gu
- UpCaseSubSubMode, // used for gU
- ReplaceSubSubMode, // used for r after visual mode
- TextObjectSubSubMode, // used for thing like iw, aW, as etc.
+ FtSubSubMode, // Used for f, F, t, T.
+ MarkSubSubMode, // Used for m.
+ BackTickSubSubMode, // Used for `.
+ TickSubSubMode, // Used for '.
+ InvertCaseSubSubMode, // Used for ~.
+ DownCaseSubSubMode, // Used for gu.
+ UpCaseSubSubMode, // Used for gU.
+ ReplaceSubSubMode, // Used for r after visual mode.
+ TextObjectSubSubMode, // Used for thing like iw, aW, as etc.
+ SearchSubSubMode,
};
enum VisualMode
MoveLineWise,
};
-enum RangeMode
-{
- RangeCharMode, // v
- RangeLineMode, // V
- RangeLineModeExclusive, // like above, but keep one newline when deleting
- RangeBlockMode, // Ctrl-v
- RangeBlockAndTailMode, // Ctrl-v for D and X
-};
+/*!
+ \enum RangeMode
+
+ The \e RangeMode serves as a means to define how the "Range" between
+ the \l cursor and the \l anchor position is to be interpreted.
+
+ \value RangeCharMode Entered by pressing \key v. The range includes
+ all characters between cursor and anchor.
+ \value RangeLineMode Entered by pressing \key V. The range includes
+ all lines between the line of the cursor and
+ the line of the anchor.
+ \value RangeLineModeExclusice Like \l RangeLineMode, but keeps one
+ newline when deleting.
+ \value RangeBlockMode Entered by pressing \key Ctrl-v. The range includes
+ all characters with line and column coordinates
+ between line and columns coordinates of cursor and
+ anchor.
+ \value RangeBlockAndTailMode Like \l RangeBlockMode, but also includes
+ all characters in the affected lines up to the end
+ of these lines.
+*/
enum EventResult
{
struct Column
{
Column(int p, int l) : physical(p), logical(l) {}
- int physical; // number of characters in the data
- int logical; // column on screen
+ int physical; // Number of characters in the data.
+ int logical; // Column on screen.
};
struct CursorPosition
struct Register
{
Register() : rangemode(RangeCharMode) {}
+ Register(const QString &c) : contents(c), rangemode(RangeCharMode) {}
Register(const QString &c, RangeMode m) : contents(c), rangemode(m) {}
QString contents;
RangeMode rangemode;
};
-struct Range
+QDebug operator<<(QDebug ts, const Register ®)
{
- Range()
- : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
- {}
+ return ts << reg.contents;
+}
- Range(int b, int e, RangeMode m = RangeCharMode)
- : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
- {}
+struct SearchData
+{
+ SearchData() { init(); }
- QString toString() const
- {
- return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
- .arg(rangemode);
- }
+ void init() { forward = true; mustMove = true; highlightMatches = true;
+ highlightCursor = true; }
- int beginPos;
- int endPos;
- RangeMode rangemode;
+ QString needle;
+ bool forward;
+ bool mustMove;
+ bool highlightMatches;
+ bool highlightCursor;
};
-QDebug &operator<<(QDebug &ts, const QList<QTextEdit::ExtraSelection> &sels)
+
+Range::Range()
+ : beginPos(-1), endPos(-1), rangemode(RangeCharMode)
+{}
+
+Range::Range(int b, int e, RangeMode m)
+ : beginPos(qMin(b, e)), endPos(qMax(b, e)), rangemode(m)
+{}
+
+QString Range::toString() const
+{
+ return QString("%1-%2 (mode: %3)").arg(beginPos).arg(endPos)
+ .arg(rangemode);
+}
+
+QDebug operator<<(QDebug ts, const Range &range)
+{
+ return ts << '[' << range.beginPos << ',' << range.endPos << ']';
+}
+
+
+
+ExCommand::ExCommand(const QString &c, const QString &a, const Range &r)
+ : cmd(c), hasBang(false), args(a), range(r)
+{}
+
+QDebug operator<<(QDebug ts, const ExCommand &cmd)
+{
+ return ts << cmd.cmd << ' ' << cmd.args << ' ' << cmd.range;
+}
+
+QDebug operator<<(QDebug ts, const QList<QTextEdit::ExtraSelection> &sels)
{
foreach (const QTextEdit::ExtraSelection &sel, sels)
ts << "SEL: " << sel.cursor.anchor() << sel.cursor.position();
{
QString res;
for (int i = 0, n = ba.size(); i != n; ++i) {
- QChar c = ba.at(i);
+ const QChar c = ba.at(i);
+ const int cc = c.unicode();
if (c.isPrint())
res += c;
+ else if (cc == '\n')
+ res += _("<CR>");
else
- res += QString("\\x%1").arg(c.unicode(), 2, 16);
+ res += QString("\\x%1").arg(c.unicode(), 2, 16, QLatin1Char('0'));
}
return res;
}
return true;
}
-inline QString msgE20MarkNotSet(const QString &text)
+inline QString msgMarkNotSet(const QString &text)
{
- return FakeVimHandler::tr("E20: Mark '%1' not set").arg(text);
+ return FakeVimHandler::tr("Mark '%1' not set").arg(text);
}
class Input
explicit Input(QChar x)
: m_key(x.unicode()), m_xkey(x.unicode()), m_modifiers(0), m_text(x) {}
- Input(int k, int m, QString t)
+ Input(int k, int m, const QString &t)
: m_key(k), m_modifiers(m), m_text(t)
{
// m_xkey is only a cache.
{
return m_xkey >= '0' && m_xkey <= '9';
}
+
bool isKey(int c) const
{
return !m_modifiers && m_key == c;
}
+ bool isBackspace() const
+ {
+ return m_key == Key_Backspace || isControl('h');
+ }
+
+ bool isReturn() const
+ {
+ return m_key == Key_Return;
+ }
+
+ bool isEscape() const
+ {
+ return isKey(Key_Escape) || isKey(27) || isControl('c')
+ || isControl(Key_BracketLeft);
+ }
+
bool is(int c) const
{
return m_xkey == c && (m_modifiers == 0 || m_modifiers == Qt::ShiftModifier);
bool operator==(const Input &a) const
{
- return a.m_key == m_key && m_text == a.m_text;
+ return a.m_key == m_key && a.m_modifiers == m_modifiers
+ && m_text == a.m_text;
}
+ bool operator!=(const Input &a) const { return !operator==(a); }
+
QString text() const { return m_text; }
+ QChar asChar() const
+ {
+ return (m_text.size() == 1 ? m_text.at(0) : QChar());
+ }
+
int key() const { return m_key; }
+ QDebug dump(QDebug ts) const
+ {
+ return ts << m_key << '-' << m_modifiers << '-'
+ << quoteUnprintable(m_text);
+ }
private:
int m_key;
int m_xkey;
QString m_text;
};
-typedef QVector<Input> Inputs;
+QDebug operator<<(QDebug ts, const Input &input) { return input.dump(ts); }
+
+class Inputs : public QVector<Input>
+{
+public:
+ Inputs() {}
+ explicit Inputs(const QString &str) { parseFrom(str); }
+ void parseFrom(const QString &str);
+};
+
+
+void Inputs::parseFrom(const QString &str)
+{
+ const int n = str.size();
+ for (int i = 0; i < n; ++i) {
+ uint c0 = str.at(i).unicode(), c1 = 0, c2 = 0, c3 = 0, c4 = 0, c5 = 0;
+ if (i + 1 < n)
+ c1 = str.at(i + 1).unicode();
+ if (i + 2 < n)
+ c2 = str.at(i + 2).unicode();
+ if (i + 3 < n)
+ c3 = str.at(i + 3).unicode();
+ if (i + 4 < n)
+ c4 = str.at(i + 4).unicode();
+ if (i + 5 < n)
+ c5 = str.at(i + 5).unicode();
+ if (c0 == '<') {
+ if ((c1 == 'C' || c1 == 'c') && c2 == '-' && c4 == '>') {
+ uint c = (c3 < 90 ? c3 : c3 - 32);
+ append(Input(c, Qt::ControlModifier, QString(QChar(c - 64))));
+ i += 4;
+ } else {
+ append(Input(QLatin1Char(c0)));
+ }
+ } else {
+ append(Input(QLatin1Char(c0)));
+ }
+ }
+}
+
+class History
+{
+public:
+ History() : m_index(0) {}
+ void append(const QString &item)
+ { //qDebug() << "APP: " << item << m_items;
+ m_items.removeAll(item);
+ m_items.append(item); m_index = m_items.size() - 1; }
+ void down() { m_index = qMin(m_index + 1, m_items.size()); }
+ void up() { m_index = qMax(m_index - 1, 0); }
+ //void clear() { m_items.clear(); m_index = 0; }
+ void restart() { m_index = m_items.size(); }
+ QString current() const { return m_items.value(m_index, QString()); }
+ QStringList items() const { return m_items; }
+private:
+ QStringList m_items;
+ int m_index;
+};
+
// Mappings for a specific mode.
-class ModeMapping : private QList<QPair<Inputs, Inputs> >
+class ModeMapping : public QList<QPair<Inputs, Inputs> >
{
public:
ModeMapping() { test(); }
}
}
- // Returns 'false' if more input input is needed to decide whether a
- // mapping needs to be applied. If a decision can be made, return 'true',
+ // Returns 'false' if more input is needed to decide whether a mapping
+ // needs to be applied. If a decision can be made, return 'true',
// and replace *input with the mapped data.
- bool mappingDone(Inputs *input) const
+ bool mappingDone(Inputs *inputs) const
{
- Q_UNUSED(input);
// FIXME: inefficient.
for (int i = 0; i != size(); ++i) {
+ const Inputs &haystack = at(i).first;
// A mapping
- if (startsWith(at(i).first, *input)) {
- if (at(i).first.size() != input->size())
+ if (startsWith(haystack, *inputs)) {
+ if (haystack.size() != inputs->size())
return false; // This can be extended.
// Actual mapping.
- *input = at(i).second;
+ *inputs = at(i).second;
return true;
}
}
- // No extensible mapping found. Use input as-is.
+ // No extensible mapping found. Use inputs as-is.
return true;
}
if (needle.size() > haystack.size())
return false;
for (int i = 0; i != needle.size(); ++i) {
- if (needle.at(i).text() != haystack.at(i).text())
+ if (needle.at(i) != haystack.at(i))
return false;
}
return true;
}
};
+
class FakeVimHandler::Private : public QObject
{
Q_OBJECT
void handleCommand(const QString &cmd); // Sets m_tc + handleExCommand
void handleExCommand(const QString &cmd);
- // updates marks positions by the difference in positionChange
+ // Updates marks positions by the difference in positionChange.
void fixMarks(int positionAction, int positionChange);
void installEventFilter();
EventResult handleKey(const Input &);
Q_SLOT EventResult handleKey2();
EventResult handleInsertMode(const Input &);
+ EventResult handleReplaceMode(const Input &);
EventResult handleCommandMode(const Input &);
EventResult handleRegisterMode(const Input &);
- EventResult handleMiniBufferModes(const Input &);
+ EventResult handleExMode(const Input &);
+ EventResult handleSearchSubSubMode(const Input &);
EventResult handleCommandSubSubMode(const Input &);
void finishMovement(const QString &dotCommand = QString());
void finishMovement(const QString &dotCommand, int count);
void resetCommandMode();
- void search(const QString &needle, bool forward, bool incSearch = false);
+ void search(const SearchData &sd);
void highlightMatches(const QString &needle);
void stopIncrementalFind();
bool atEndOfLine() const
{ return m_tc.atBlockEnd() && m_tc.block().length() > 1; }
- int lastPositionInDocument() const; // last valid pos in doc
+ int lastPositionInDocument() const; // Returns last valid position in doc.
int firstPositionInLine(int line) const; // 1 based line, 0 based pos
int lastPositionInLine(int line) const; // 1 based line, 0 based pos
int lineForPosition(int pos) const; // 1 based line, 0 based pos
QString lineContents(int line) const; // 1 based line
- void setLineContents(int line, const QString &contents) const; // 1 based line
+ void setLineContents(int line, const QString &contents); // 1 based line
int linesOnScreen() const;
int columnsOnScreen() const;
int linesInDocument() const;
- // all zero-based counting
+ // The following use all zero-based counting.
int cursorLineOnScreen() const;
int cursorLineInDocument() const;
int physicalCursorColumnInDocument() const; // as stored in the data
void setCursorPosition(const CursorPosition &p)
{ setPosition(p.position); scrollToLineInDocument(p.scrollLine); }
- // helper functions for indenting
+ // Helper functions for indenting/
bool isElectricCharacter(QChar c) const;
void indentSelectedText(QChar lastTyped = QChar());
int indentText(const Range &range, QChar lastTyped = QChar());
void moveToMatchingParanthesis();
void moveToWordBoundary(bool simple, bool forward, bool changeWord = false);
- // to reduce line noise
+ // Convenience wrappers to reduce line noise.
void moveToEndOfDocument() { m_tc.movePosition(EndOfDocument, MoveAnchor); }
void moveToStartOfLine();
void moveToEndOfLine();
bool handleFfTt(QString key);
- // helper function for handleExCommand. return 1 based line index.
+ // Helper function for handleExCommand returning 1 based line index.
int readLineCode(QString &cmd);
- void selectRange(int beginLine, int endLine);
void enterInsertMode();
+ void enterReplaceMode();
void enterCommandMode();
void enterExMode();
void showRedMessage(const QString &msg);
void notImplementedYet();
void updateMiniBuffer();
void updateSelection();
+ void updateCursor();
QWidget *editor() const;
QChar characterAtCursor() const
{ return m_tc.document()->characterAt(m_tc.position()); }
void beginEditBlock() { UNDO_DEBUG("BEGIN EDIT BLOCK"); m_tc.beginEditBlock(); }
void beginEditBlock(int pos) { setUndoPosition(pos); beginEditBlock(); }
void endEditBlock() { UNDO_DEBUG("END EDIT BLOCK"); m_tc.endEditBlock(); }
- void joinPreviousEditBlock() { UNDO_DEBUG("JOIN EDIT BLOCK"); m_tc.joinPreviousEditBlock(); }
-
- // this asks the layer above (e.g. the fake vim plugin or the
- // stand-alone test application to handle the command)
- void passUnknownExCommand(const QString &cmd);
- // this asks the layer above (e.g. the fake vim plugin or the
- // stand-alone test application to handle the set command)
- void passUnknownSetCommand(const QString &cmd);
+ void joinPreviousEditBlock() { UNDO_DEBUG("JOIN"); m_tc.joinPreviousEditBlock(); }
+ void breakEditBlock()
+ { m_tc.beginEditBlock(); m_tc.insertText("x");
+ m_tc.deletePreviousChar(); m_tc.endEditBlock(); }
bool isVisualMode() const { return m_visualMode != NoVisualMode; }
bool isNoVisualMode() const { return m_visualMode == NoVisualMode; }
void selectBlockTextObject(bool inner, char left, char right);
void selectQuotedStringTextObject(bool inner, int type);
+ Q_SLOT void importSelection();
+
public:
QTextEdit *m_textedit;
QPlainTextEdit *m_plaintextedit;
QTextCursor m_tc;
int m_oldPosition; // copy from last event to check for external changes
int m_anchor;
- static QHash<int, Register> m_registers;
int m_register;
QString m_mvcount;
QString m_opcount;
bool m_anchorPastEnd;
bool m_positionPastEnd; // '$' & 'l' in visual mode can move past eol
- bool isSearchMode() const
- { return m_mode == SearchForwardMode || m_mode == SearchBackwardMode; }
int m_gflag; // whether current command started with 'g'
+ QString m_commandPrefix;
QString m_commandBuffer;
QString m_currentFileName;
QString m_currentMessage;
bool m_lastSearchForward;
bool m_findPending;
QString m_lastInsertion;
+ QString m_lastDeletion;
int anchor() const { return m_anchor; }
int position() const { return m_tc.position(); }
- typedef void (FakeVimHandler::Private::*Transformation)(int, QTextCursor *);
- void transformText(const Range &range, Transformation transformation);
-
- void removeSelectedText();
+ struct TransformationData
+ {
+ TransformationData(const QString &s, const QVariant &d)
+ : from(s), extraData(d) {}
+ QString from;
+ QString to;
+ QVariant extraData;
+ };
+ typedef void (Private::*Transformation)(TransformationData *td);
+ void transformText(const Range &range, Transformation transformation,
+ const QVariant &extraData = QVariant());
+
+ void insertText(const Register ®);
void removeText(const Range &range);
- void removeTransform(int, QTextCursor *);
+ void removeTransform(TransformationData *td);
- void invertCaseSelectedText();
- void invertCaseTransform(int, QTextCursor *);
+ void invertCase(const Range &range);
+ void invertCaseTransform(TransformationData *td);
- void upCaseSelectedText();
- void upCaseTransform(int, QTextCursor *);
+ void upCase(const Range &range);
+ void upCaseTransform(TransformationData *td);
- void downCaseSelectedText();
- void downCaseTransform(int, QTextCursor *);
+ void downCase(const Range &range);
+ void downCaseTransform(TransformationData *td);
- QChar m_replacingCharacter;
- void replaceSelectedText(); // replace each character with m_replacingCharacter
- void replaceTransform(int, QTextCursor *);
+ QString m_replacement;
+ void replaceText(const Range &range, const QString &str);
+ void replaceTransform(TransformationData *td);
- QString selectedText() const { return text(Range(position(), anchor())); }
- QString text(const Range &range) const;
+ QString selectText(const Range &range) const;
+ void setCurrentRange(const Range &range);
+ Range currentRange() const { return Range(position(), anchor(), m_rangemode); }
- void yankSelectedText();
void yankText(const Range &range, int toregister = '"');
void pasteText(bool afterCursor);
void redo();
void setUndoPosition(int pos);
QMap<int, int> m_undoCursorPosition; // revision -> position
- bool m_beginEditBlock;
// extra data for '.'
void replay(const QString &text, int count);
- void setDotCommand(const QString &cmd) { m_dotCommand = cmd; }
- void setDotCommand(const QString &cmd, int n) { m_dotCommand = cmd.arg(n); }
- QString m_dotCommand;
- bool m_inReplay; // true if we are executing a '.'
+ void setDotCommand(const QString &cmd) { g.dotCommand = cmd; }
+ void setDotCommand(const QString &cmd, int n) { g.dotCommand = cmd.arg(n); }
// extra data for ';'
QString m_semicolonCount;
Input m_semicolonType; // 'f', 'F', 't', 'T'
QString m_semicolonKey;
- // history for '/'
- QString lastSearchString() const;
- static QStringList m_searchHistory;
- int m_searchHistoryIndex;
-
- // history for ':'
- static QStringList m_commandHistory;
- int m_commandHistoryIndex;
-
// visual line mode
void enterVisualMode(VisualMode visualMode);
void leaveVisualMode();
VisualMode m_visualMode;
// marks as lines
+ int mark(int code) const;
+ void setMark(int code, int position);
QHash<int, int> m_marks;
- QString m_oldNeedle;
// vi style configuration
QVariant config(int code) const { return theFakeVimSetting(code)->value(); }
int m_targetColumn; // -1 if past end of line
int m_visualTargetColumn; // 'l' can move past eol in visual mode only
-
int m_cursorWidth;
// auto-indent
void handleStartOfLine();
void recordJump();
- void recordNewUndo();
QVector<CursorPosition> m_jumpListUndo;
QVector<CursorPosition> m_jumpListRedo;
QList<QTextEdit::ExtraSelection> m_searchSelections;
+ QTextCursor m_searchCursor;
+ QString m_oldNeedle;
- bool handleExCommandHelper(const QString &cmd); // Returns success.
- QString extractCommand(const QString &line, int *beginLine, int *endLine);
- bool handleExBangCommand(const QString &line);
- bool handleExDeleteCommand(const QString &line);
- bool handleExGotoCommand(const QString &line);
- bool handleExHistoryCommand(const QString &line);
- bool handleExMapCommand(const QString &line);
- bool handleExNormalCommand(const QString &line);
- bool handleExReadCommand(const QString &line);
- bool handleExRedoCommand(const QString &line);
- bool handleExSetCommand(const QString &line);
- bool handleExShiftRightCommand(const QString &line);
- bool handleExSourceCommand(const QString &line);
- bool handleExSubstituteCommand(const QString &line);
- bool handleExWriteCommand(const QString &line);
-
- // All mappings.
- typedef QHash<char, ModeMapping> Mappings;
- static Mappings m_mappings;
-
- QVector<Input> m_pendingInput;
+ bool handleExCommandHelper(const ExCommand &cmd); // Returns success.
+ bool handleExPluginCommand(const ExCommand &cmd); // Handled by plugin?
+ bool handleExBangCommand(const ExCommand &cmd);
+ bool handleExDeleteCommand(const ExCommand &cmd);
+ bool handleExGotoCommand(const ExCommand &cmd);
+ bool handleExHistoryCommand(const ExCommand &cmd);
+ bool handleExRegisterCommand(const ExCommand &cmd);
+ bool handleExMapCommand(const ExCommand &cmd);
+ bool handleExNormalCommand(const ExCommand &cmd);
+ bool handleExReadCommand(const ExCommand &cmd);
+ bool handleExRedoCommand(const ExCommand &cmd);
+ bool handleExSetCommand(const ExCommand &cmd);
+ bool handleExShiftCommand(const ExCommand &cmd);
+ bool handleExSourceCommand(const ExCommand &cmd);
+ bool handleExSubstituteCommand(const ExCommand &cmd);
+ bool handleExWriteCommand(const ExCommand &cmd);
void timerEvent(QTimerEvent *ev);
- int m_inputTimer;
void setupCharClass();
int charClass(QChar c, bool simple) const;
signed char m_charClass[256];
+
+ static struct GlobalData
+ {
+ GlobalData()
+ {
+ inReplay = false;
+ inputTimer = -1;
+ }
+
+ // Input.
+ Inputs pendingInput;
+ int inputTimer;
+
+ // Repetition.
+ QString dotCommand;
+ bool inReplay; // true if we are executing a '.'
+
+ // History for searches.
+ History searchHistory;
+
+ // History for :ex commands.
+ History commandHistory;
+
+ QHash<int, Register> registers;
+
+ // All mappings.
+ typedef QHash<char, ModeMapping> Mappings;
+ Mappings mappings;
+ } g;
};
-QStringList FakeVimHandler::Private::m_searchHistory;
-QStringList FakeVimHandler::Private::m_commandHistory;
-QHash<int, Register> FakeVimHandler::Private::m_registers;
-FakeVimHandler::Private::Mappings FakeVimHandler::Private::m_mappings;
+FakeVimHandler::Private::GlobalData FakeVimHandler::Private::g;
FakeVimHandler::Private::Private(FakeVimHandler *parent, QWidget *widget)
{
m_movetype = MoveInclusive;
m_anchor = 0;
m_cursorWidth = EDITOR(cursorWidth());
- m_inReplay = false;
m_justAutoIndented = 0;
m_rangemode = RangeCharMode;
- m_beginEditBlock = true;
- m_inputTimer = -1;
setupCharClass();
}
KEY_DEBUG("SHORTCUT OVERRIDE" << key << " PASSING: " << m_passing);
if (key == Key_Escape) {
+ if (m_subsubmode == SearchSubSubMode)
+ return true;
// Not sure this feels good. People often hit Esc several times
if (isNoVisualMode() && m_mode == CommandMode)
return false;
// Try to compensate for code completion
if (dist > 0 && dist <= physicalCursorColumnInDocument()) {
Range range(m_oldPosition, m_tc.position());
- m_lastInsertion.append(text(range));
+ m_lastInsertion.append(selectText(range));
}
} else if (!isVisualMode()) {
if (atEndOfLine())
// key = shift(key);
//}
- QTC_ASSERT(
- !(m_mode != InsertMode && m_tc.atBlockEnd() && m_tc.block().length() > 1),
+ QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
+ || !m_tc.atBlockEnd() || m_tc.block().length() <= 1,
qDebug() << "Cursor at EOL before key handler");
EventResult result = handleKey(Input(key, mods, ev->text()));
// We fake vi-style end-of-line behaviour
m_fakeEnd = atEndOfLine() && m_mode == CommandMode && !isVisualBlockMode();
- QTC_ASSERT(
- !(m_mode != InsertMode && m_tc.atBlockEnd() && m_tc.block().length() > 1),
+ QTC_ASSERT(m_mode == InsertMode || m_mode == ReplaceMode
+ || !m_tc.atBlockEnd() || m_tc.block().length() <= 1,
qDebug() << "Cursor at EOL after key handler");
if (m_fakeEnd)
EDITOR(setTextCursor(m_tc));
m_oldPosition = m_tc.position();
}
+
+ if (hasConfig(ConfigShowMarks))
+ updateSelection();
+
return result;
}
void FakeVimHandler::Private::installEventFilter()
{
+ EDITOR(viewport()->installEventFilter(q));
EDITOR(installEventFilter(q));
}
void FakeVimHandler::Private::setupWidget()
{
enterCommandMode();
- //EDITOR(setCursorWidth(QFontMetrics(ed->font()).width(QChar('x')));
if (m_textedit) {
m_textedit->setLineWrapMode(QTextEdit::NoWrap);
} else if (m_plaintextedit) {
m_plaintextedit->setLineWrapMode(QPlainTextEdit::NoWrap);
}
m_wasReadOnly = EDITOR(isReadOnly());
- //EDITOR(setReadOnly(true));
updateEditor();
+ importSelection();
+ updateMiniBuffer();
+ updateCursor();
+}
+void FakeVimHandler::Private::importSelection()
+{
QTextCursor tc = EDITOR(textCursor());
+ int pos = tc.position();
+ int anc = tc.anchor();
if (tc.hasSelection()) {
- int pos = tc.position();
- int anc = tc.anchor();
- m_marks['<'] = anc;
- m_marks['>'] = pos;
- m_anchor = anc;
- m_visualMode = VisualCharMode;
- tc.clearSelection();
- EDITOR(setTextCursor(tc));
- m_tc = tc; // needed in updateSelection
- updateSelection();
+ // FIXME: Why?
+ if (pos < anc)
+ --anc;
+ else
+ tc.movePosition(Left, KeepAnchor);
}
-
- updateMiniBuffer();
+ setMark('<', anc);
+ setMark('>', pos);
+ m_anchor = anc;
+ Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
+ if (!tc.hasSelection())
+ m_visualMode = NoVisualMode;
+ else if (mods & Qt::ControlModifier)
+ m_visualMode = VisualBlockMode;
+ else if (mods & Qt::AltModifier)
+ m_visualMode = VisualBlockMode;
+ else if (mods & Qt::ShiftModifier)
+ m_visualMode = VisualLineMode;
+ else
+ m_visualMode = VisualCharMode;
+ m_tc = tc; // needed in updateSelection
+ tc.clearSelection();
+ EDITOR(setTextCursor(tc));
+ updateSelection();
}
void FakeVimHandler::Private::updateEditor()
//showBlackMessage(QString());
//updateMiniBuffer();
//EDITOR(removeEventFilter(q));
- EDITOR(setReadOnly(m_wasReadOnly));
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(false));
+ //EDITOR(setReadOnly(m_wasReadOnly));
const int charWidth = QFontMetrics(EDITOR(font())).width(QChar(' '));
EDITOR(setTabStopWidth(charWidth * tabSize));
if (isVisualLineMode()) {
m_tc = EDITOR(textCursor());
- int beginLine = lineForPosition(m_marks['<']);
- int endLine = lineForPosition(m_marks['>']);
+ int beginLine = lineForPosition(mark('<'));
+ int endLine = lineForPosition(mark('>'));
m_tc.setPosition(firstPositionInLine(beginLine), MoveAnchor);
m_tc.setPosition(lastPositionInLine(endLine), KeepAnchor);
EDITOR(setTextCursor(m_tc));
- } else if (isVisualCharMode()) {
+ } else if (isVisualCharMode() || isVisualBlockMode()) {
m_tc = EDITOR(textCursor());
- m_tc.setPosition(m_marks['<'], MoveAnchor);
- m_tc.setPosition(m_marks['>'], KeepAnchor);
+ m_tc.setPosition(mark('<'), MoveAnchor);
+ m_tc.setPosition(mark('>'), KeepAnchor);
EDITOR(setTextCursor(m_tc));
}
m_visualMode = NoVisualMode;
+ // Force "ordinary" cursor.
+ m_mode = InsertMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ updateCursor();
updateSelection();
}
EventResult FakeVimHandler::Private::handleKey(const Input &input)
{
- if (m_mode == InsertMode || m_mode == CommandMode) {
- m_pendingInput.append(input);
+ KEY_DEBUG("HANDLE INPUT: " << input);
+ if (m_mode == ExMode)
+ return handleExMode(input);
+ if (m_subsubmode == SearchSubSubMode)
+ return handleSearchSubSubMode(input);
+ if (m_mode == InsertMode || m_mode == ReplaceMode || m_mode == CommandMode) {
+ g.pendingInput.append(input);
const char code = m_mode == InsertMode ? 'i' : 'n';
- if (m_mappings[code].mappingDone(&m_pendingInput))
+ if (g.mappings[code].mappingDone(&g.pendingInput))
return handleKey2();
- if (m_inputTimer != -1)
- killTimer(m_inputTimer);
- m_inputTimer = startTimer(1000);
+ if (g.inputTimer != -1)
+ killTimer(g.inputTimer);
+ g.inputTimer = startTimer(1000);
return EventHandled;
}
- if (m_mode == ExMode || m_mode == SearchForwardMode
- || m_mode == SearchBackwardMode)
- return handleMiniBufferModes(input);
return EventUnhandled;
}
setUndoPosition(m_tc.position());
if (m_mode == InsertMode) {
EventResult result = EventUnhandled;
- foreach (const Input &in, m_pendingInput) {
+ foreach (const Input &in, g.pendingInput) {
EventResult r = handleInsertMode(in);
if (r == EventHandled)
result = EventHandled;
}
- m_pendingInput.clear();
+ g.pendingInput.clear();
+ return result;
+ }
+ if (m_mode == ReplaceMode) {
+ EventResult result = EventUnhandled;
+ foreach (const Input &in, g.pendingInput) {
+ EventResult r = handleReplaceMode(in);
+ if (r == EventHandled)
+ result = EventHandled;
+ }
+ g.pendingInput.clear();
return result;
}
if (m_mode == CommandMode) {
EventResult result = EventUnhandled;
- foreach (const Input &in, m_pendingInput) {
+ foreach (const Input &in, g.pendingInput) {
EventResult r = handleCommandMode(in);
if (r == EventHandled)
result = EventHandled;
}
- m_pendingInput.clear();
+ g.pendingInput.clear();
return result;
}
return EventUnhandled;
if (!isVisualMode()) {
m_anchor = m_tc.position();
} else {
- // m_marks['<'] = m_tc.position();
+ // setMark('<', m_tc.position());
}
}
enterExMode();
m_currentMessage.clear();
m_commandBuffer = QString(".,+%1!").arg(qAbs(endLine - beginLine));
- m_commandHistory.append(QString());
- m_commandHistoryIndex = m_commandHistory.size() - 1;
+ //g.commandHistory.append(QString());
updateMiniBuffer();
+ updateCursor();
return;
}
if (isVisualMode())
- m_marks['>'] = m_tc.position();
+ setMark('>', m_tc.position());
if (m_submode == ChangeSubMode
|| m_submode == DeleteSubMode
if (m_movetype == MoveInclusive) {
if (anchor() <= position()) {
- if ( !m_tc.atBlockEnd())
+ if (!m_tc.atBlockEnd())
moveRight(); // correction
} else {
m_anchor++;
}
if (m_submode != TransformSubMode) {
- yankSelectedText();
+ yankText(currentRange(), m_register);
if (m_movetype == MoveLineWise)
- m_registers[m_register].rangemode = RangeLineMode;
+ g.registers[m_register].rangemode = RangeLineMode;
}
m_positionPastEnd = m_anchorPastEnd = false;
if (m_submode == ChangeSubMode) {
if (m_rangemode == RangeLineMode)
m_rangemode = RangeLineModeExclusive;
- removeSelectedText();
+ removeText(currentRange());
if (!dotCommand.isEmpty())
setDotCommand(QLatin1Char('c') + dotCommand);
if (m_movetype == MoveLineWise)
insertAutomaticIndentation(true);
endEditBlock();
+ setUndoPosition(position());
enterInsertMode();
- m_beginEditBlock = false;
m_submode = NoSubMode;
} else if (m_submode == DeleteSubMode) {
- removeSelectedText();
+ removeText(currentRange());
if (!dotCommand.isEmpty())
setDotCommand(QLatin1Char('d') + dotCommand);
if (m_movetype == MoveLineWise)
const int la = lineForPosition(anchor());
const int lp = lineForPosition(position());
if (m_register != '"') {
- setPosition(m_marks[m_register]);
+ setPosition(mark(m_register));
moveToStartOfLine();
} else {
if (anchor() <= position())
showBlackMessage(QString("%1 lines yanked").arg(qAbs(la - lp) + 1));
} else if (m_submode == TransformSubMode) {
if (m_subsubmode == InvertCaseSubSubMode) {
- invertCaseSelectedText();
+ invertCase(currentRange());
if (!dotCommand.isEmpty())
setDotCommand(QLatin1Char('~') + dotCommand);
} else if (m_subsubmode == UpCaseSubSubMode) {
- upCaseSelectedText();
+ upCase(currentRange());
if (!dotCommand.isEmpty())
setDotCommand("gU" + dotCommand);
} else if (m_subsubmode == DownCaseSubSubMode) {
- downCaseSelectedText();
+ downCase(currentRange());
if (!dotCommand.isEmpty())
setDotCommand("gu" + dotCommand);
} else if (m_subsubmode == ReplaceSubSubMode) {
- replaceSelectedText();
+ replaceText(currentRange(), m_replacement);
if (!dotCommand.isEmpty())
setDotCommand("r" + dotCommand);
}
if (m_movetype == MoveLineWise)
handleStartOfLine();
endEditBlock();
- } else if (m_submode == ReplaceSubMode) {
- m_submode = NoSubMode;
} else if (m_submode == IndentSubMode) {
recordJump();
beginEditBlock();
indentSelectedText();
endEditBlock();
m_submode = NoSubMode;
- updateMiniBuffer();
} else if (m_submode == ShiftRightSubMode) {
recordJump();
shiftRegionRight(1);
m_submode = NoSubMode;
- updateMiniBuffer();
} else if (m_submode == ShiftLeftSubMode) {
recordJump();
shiftRegionLeft(1);
m_submode = NoSubMode;
- updateMiniBuffer();
}
resetCommandMode();
+ updateSelection();
+ updateMiniBuffer();
+ updateCursor();
}
void FakeVimHandler::Private::resetCommandMode()
m_register = '"';
m_tc.clearSelection();
m_rangemode = RangeCharMode;
-
- updateSelection();
- updateMiniBuffer();
}
void FakeVimHandler::Private::updateSelection()
{
QList<QTextEdit::ExtraSelection> selections = m_searchSelections;
+ if (!m_searchCursor.isNull()) {
+ QTextEdit::ExtraSelection sel;
+ sel.cursor = m_searchCursor;
+ sel.format = m_searchCursor.blockCharFormat();
+ sel.format.setForeground(Qt::white);
+ sel.format.setBackground(Qt::black);
+ selections.append(sel);
+ }
if (isVisualMode()) {
QTextEdit::ExtraSelection sel;
sel.cursor = m_tc;
sel.format.setBackground(Qt::black);
#endif
const int cursorPos = m_tc.position();
- const int anchorPos = m_marks['<'];
+ const int anchorPos = mark('<');
//qDebug() << "POS: " << cursorPos << " ANCHOR: " << anchorPos;
if (isVisualCharMode()) {
sel.cursor.setPosition(qMin(cursorPos, anchorPos), MoveAnchor);
}
}
//qDebug() << "SELECTION: " << selections;
+ if (hasConfig(ConfigShowMarks)) {
+ for (QHashIterator<int, int> it(m_marks); it.hasNext(); ) {
+ it.next();
+ QTextEdit::ExtraSelection sel;
+ sel.cursor = m_tc;
+ sel.cursor.setPosition(it.value(), MoveAnchor);
+ sel.cursor.setPosition(it.value() + 1, KeepAnchor);
+ sel.format = m_tc.blockCharFormat();
+ sel.format.setForeground(Qt::blue);
+ sel.format.setBackground(Qt::green);
+ selections.append(sel);
+ }
+ }
emit q->selectionChanged(selections);
}
msg = "-- VISUAL BLOCK --";
}
} else if (m_mode == InsertMode) {
- if (m_submode == ReplaceSubMode)
- msg = "-- REPLACE --";
- else
- msg = "-- INSERT --";
- } else {
- if (m_mode == SearchForwardMode)
- msg += '/';
- else if (m_mode == SearchBackwardMode)
- msg += '?';
- else if (m_mode == ExMode)
- msg += ':';
+ msg = "-- INSERT --";
+ } else if (m_mode == ReplaceMode) {
+ msg = "-- REPLACE --";
+ } else if (!m_commandPrefix.isEmpty()) {
+ //QTC_ASSERT(m_mode == ExMode || m_subsubmode == SearchSubSubMode,
+ // qDebug() << "MODE: " << m_mode << m_subsubmode);
+ msg = m_commandPrefix;
foreach (QChar c, m_commandBuffer) {
if (c.unicode() < 32) {
msg += '^';
}
if (!msg.isEmpty() && m_mode != CommandMode)
msg += QChar(10073); // '|'; // FIXME: Use a real "cursor"
+ } else {
+ QTC_ASSERT(m_mode == CommandMode && m_subsubmode != SearchSubSubMode, /**/);
+ msg = "-- COMMAND --";
}
emit q->commandBufferChanged(msg);
int linesInDoc = linesInDocument();
int l = cursorLineInDocument();
QString status;
- const QString pos = QString::fromLatin1("%1,%2").arg(l + 1).arg(physicalCursorColumnInDocument() + 1);
+ const QString pos = QString::fromLatin1("%1,%2")
+ .arg(l + 1).arg(physicalCursorColumnInDocument() + 1);
// FIXME: physical "-" logical
if (linesInDoc != 0) {
status = FakeVimHandler::tr("%1%2%").arg(pos, -10).arg(l * 100 / linesInDoc, 4);
m_rangemode = RangeLineMode;
else if (isVisualBlockMode())
m_rangemode = RangeBlockMode;
- if (!input.text().isEmpty() && input.text().at(0).isPrint()) {
+ // FIXME: Consolidate.
+ if (isVisualMode()) {
leaveVisualMode();
- m_replacingCharacter = input.text().at(0);
+ m_replacement = input.text();
+ finishMovement();
+ } else {
+ Range range(position(), position() + count(), RangeCharMode);
+ m_replacement = input.text();
+ Transformation tr = &FakeVimHandler::Private::replaceTransform;
+ transformText(range, tr);
+ m_subsubmode = NoSubSubMode;
+ m_submode = NoSubMode;
+ setDotCommand("%1r" + input.text(), count());
finishMovement();
}
} else if (m_subsubmode == MarkSubSubMode) {
- m_marks[input.key()] = m_tc.position();
+ setMark(input.asChar().unicode(), m_tc.position());
m_subsubmode = NoSubSubMode;
} else if (m_subsubmode == BackTickSubSubMode
|| m_subsubmode == TickSubSubMode) {
- if (m_marks.contains(input.key())) {
- setPosition(m_marks[input.key()]);
+ int m = mark(input.asChar().unicode());
+ if (m != -1) {
+ setPosition(m);
if (m_subsubmode == TickSubSubMode)
moveToFirstNonBlankOnLine();
finishMovement();
} else {
- showRedMessage(msgE20MarkNotSet(input.text()));
+ showRedMessage(msgMarkNotSet(input.text()));
}
m_subsubmode = NoSubSubMode;
} else {
{
EventResult handled = EventHandled;
- if (input.isKey(Key_Escape) || input.isControl(Key_BracketLeft)) {
+ if (input.isEscape()) {
if (isVisualMode()) {
leaveVisualMode();
} else if (m_submode != NoSubMode) {
finishMovement();
} else {
resetCommandMode();
+ updateSelection();
+ updateMiniBuffer();
}
} else if (m_subsubmode != NoSubSubMode) {
handleCommandSubSubMode(input);
emit q->windowCommandRequested(input.key());
m_submode = NoSubMode;
} else if (m_submode == RegisterSubMode) {
- m_register = input.key();
+ m_register = input.asChar().unicode();
m_submode = NoSubMode;
m_rangemode = RangeLineMode;
} else if (m_submode == ChangeSubMode && input.is('c')) { // tested
finishMovement();
} else if (m_submode == ZSubMode) {
//qDebug() << "Z_MODE " << cursorLineInDocument() << linesOnScreen();
- if (input.isKey(Key_Return) || input.is('t')) {
+ if (input.isReturn() || input.is('t')) {
// Cursor line to top of window.
if (!m_mvcount.isEmpty())
setPosition(firstPositionInLine(count()));
scrollUp(- cursorLineOnScreen());
- if (input.isKey(Key_Return))
+ if (input.isReturn())
moveToFirstNonBlankOnLine();
finishMovement();
} else if (input.is('.') || input.is('z')) {
handleExCommand(QString(QLatin1Char('x')));
else if (input.is('Q'))
handleExCommand("q!");
- } else if (m_submode == ReplaceSubMode) {
- const QString text = input.text();
- if (count() <= (rightDist() + atEndOfLine()) && text.size() == 1
- && (text.at(0).isPrint() || text.at(0).isSpace())) {
- if (atEndOfLine())
- moveLeft();
- setAnchor();
- moveRight(count());
- removeSelectedText();
- m_tc.insertText(QString(count(), text.at(0)));
- m_movetype = MoveExclusive;
- setDotCommand("%1r" + text, count());
- moveLeft();
- }
- setTargetColumn();
- m_submode = NoSubMode;
- finishMovement();
} else if (input.isDigit()) {
if (input.is('0') && m_mvcount.isEmpty()) {
m_movetype = MoveExclusive;
finishMovement();
} else if (input.is(':')) {
enterExMode();
+ g.commandHistory.restart();
m_currentMessage.clear();
m_commandBuffer.clear();
if (isVisualMode())
m_commandBuffer = "'<,'>";
- m_commandHistory.append(QString());
- m_commandHistoryIndex = m_commandHistory.size() - 1;
updateMiniBuffer();
} else if (input.is('/') || input.is('?')) {
+ m_lastSearchForward = input.is('/');
+ g.searchHistory.restart();
if (hasConfig(ConfigUseCoreSearch)) {
// re-use the core dialog.
m_findPending = true;
- m_lastSearchForward = (input.is('/'));
EDITOR(setTextCursor(m_tc));
emit q->findRequested(!m_lastSearchForward);
m_tc = EDITOR(textCursor());
} else {
// FIXME: make core find dialog sufficiently flexible to
// produce the "default vi" behaviour too. For now, roll our own.
- enterExMode(); // to get the cursor disabled
m_currentMessage.clear();
- m_mode = (input.is('/')) ? SearchForwardMode : SearchBackwardMode;
- m_commandBuffer.clear();
- m_searchHistory.append(QString());
- m_searchHistoryIndex = m_searchHistory.size() - 1;
+ m_movetype = MoveExclusive;
+ m_subsubmode = SearchSubSubMode;
+ m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
+ m_commandBuffer = QString();
+ updateCursor();
updateMiniBuffer();
}
} else if (input.is('`')) {
m_subsubmode = BackTickSubSubMode;
} else if (input.is('#') || input.is('*')) {
// FIXME: That's not proper vim behaviour
- m_tc.select(QTextCursor::WordUnderCursor);
- QString needle = "\\<" + m_tc.selection().toPlainText() + "\\>";
- m_searchHistory.append(needle);
- m_lastSearchForward = (input.is('*'));
- updateMiniBuffer();
- search(needle, m_lastSearchForward);
- recordJump();
+ QTextCursor tc = m_tc;
+ tc.select(QTextCursor::WordUnderCursor);
+ QString needle = "\\<" + tc.selection().toPlainText() + "\\>";
+ g.searchHistory.append(needle);
+ m_lastSearchForward = input.is('*');
+ m_currentMessage.clear();
+ m_commandPrefix = QLatin1Char(m_lastSearchForward ? '/' : '?');
+ m_commandBuffer = needle;
+ SearchData sd;
+ sd.needle = needle;
+ sd.forward = m_lastSearchForward;
+ sd.highlightCursor = false;
+ sd.highlightMatches = true;
+ search(sd);
+ //m_searchCursor = QTextCursor();
+ //updateSelection();
+ //updateMiniBuffer();
} else if (input.is('\'')) {
m_subsubmode = TickSubSubMode;
} else if (input.is('|')) {
enterExMode();
m_currentMessage.clear();
m_commandBuffer = "'<,'>!";
- m_commandHistory.append(QString());
- m_commandHistoryIndex = m_commandHistory.size() - 1;
+ //g.commandHistory.append(QString());
updateMiniBuffer();
} else if (input.is('"')) {
m_submode = RegisterSubMode;
- } else if (input.isKey(Key_Return)) {
+ } else if (input.isReturn()) {
moveToStartOfLine();
moveDown();
moveToFirstNonBlankOnLine();
} else if (input.is(',')) {
passShortcuts(true);
} else if (input.is('.')) {
- //qDebug() << "REPEATING" << quoteUnprintable(m_dotCommand) << count();
- QString savedCommand = m_dotCommand;
- m_dotCommand.clear();
+ //qDebug() << "REPEATING" << quoteUnprintable(g.dotCommand) << count()
+ // << input;
+ QString savedCommand = g.dotCommand;
+ g.dotCommand.clear();
replay(savedCommand, count());
enterCommandMode();
- m_dotCommand = savedCommand;
+ g.dotCommand = savedCommand;
} else if (input.is('<') && isNoVisualMode()) {
m_submode = ShiftLeftSubMode;
} else if (input.is('<') && isVisualMode()) {
finishMovement();
} else if ((!isVisualMode() && input.is('a')) || (isVisualMode() && input.is('A'))) {
leaveVisualMode();
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
m_lastInsertion.clear();
if (!atEndOfLine())
moveRight();
updateMiniBuffer();
} else if (input.is('A')) {
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
moveBehindEndOfLine();
setDotCommand(QString(QLatin1Char('A')));
} else if ((input.is('d') || input.is('x')) && isVisualLineMode()) {
leaveVisualMode();
m_rangemode = RangeLineMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
handleStartOfLine();
} else if ((input.is('d') || input.is('x')) && isVisualBlockMode()) {
leaveVisualMode();
m_rangemode = RangeBlockMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
setPosition(qMin(position(), anchor()));
} else if (input.is('D') && isNoVisualMode()) {
if (atEndOfLine())
leaveVisualMode();
m_rangemode = RangeLineMode;
m_submode = NoSubMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
moveToFirstNonBlankOnLine();
} else if ((input.is('D') || input.is('X')) && isVisualBlockMode()) {
leaveVisualMode();
m_rangemode = RangeBlockAndTailMode;
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
setPosition(qMin(position(), anchor()));
} else if (input.isControl('d')) {
int sline = cursorLineOnScreen();
m_tc.setPosition(firstPositionInLine(n), KeepAnchor);
}
finishMovement(dotCommand);
- } else if (input.is('h') || input.isKey(Key_Left)
- || input.isKey(Key_Backspace) || input.isControl('h')) {
+ } else if (input.is('h') || input.isKey(Key_Left) || input.isBackspace()) {
m_movetype = MoveExclusive;
int n = qMin(count(), leftDist());
if (m_fakeEnd && m_tc.block().length() > 1)
finishMovement();
} else if (!isVisualMode() && (input.is('i') || input.isKey(Key_Insert))) {
setDotCommand(QString(QLatin1Char('i'))); // setDotCommand("%1i", count());
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
updateMiniBuffer();
if (atEndOfLine())
m_gflag = false;
m_tc.clearSelection();
}
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
} else if (input.isControl('i')) {
if (!m_jumpListRedo.isEmpty()) {
setAnchor();
moveRight();
if (m_gflag) {
- removeSelectedText();
+ removeText(currentRange());
} else {
while (characterAtCursor() == ' '
|| characterAtCursor() == '\t')
moveRight();
- removeSelectedText();
+ removeText(currentRange());
m_tc.insertText(QString(QLatin1Char(' ')));
}
}
handleStartOfLine();
finishMovement();
} else if (input.is('n') || input.is('N')) {
- search(lastSearchString(), m_lastSearchForward);
- recordJump();
+ SearchData sd;
+ sd.needle = g.searchHistory.current();
+ sd.forward = input.is('n') ? m_lastSearchForward : !m_lastSearchForward;
+ sd.highlightCursor = false;
+ sd.highlightMatches = true;
+ search(sd);
} else if (isVisualMode() && (input.is('o') || input.is('O'))) {
int pos = position();
setPosition(anchor());
if (m_positionPastEnd)
m_visualTargetColumn = -1;
updateSelection();
- } else if (input.is('o') || input.is('O')) {
- beginEditBlock();
+ } else if (input.is('o')) {
setDotCommand("%1o", count());
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
- m_beginEditBlock = false;
+ beginEditBlock(position());
moveToFirstNonBlankOnLine();
- if (input.is('O'))
- moveToStartOfLine();
- else
- moveBehindEndOfLine();
- m_tc.insertText("\n");
- if (input.is('O'))
- moveUp();
- insertAutomaticIndentation(input.is('o'));
+ moveBehindEndOfLine();
+ insertText(Register("\n"));
+ insertAutomaticIndentation(true);
+ endEditBlock();
+ } else if (input.is('O')) {
+ setDotCommand("%1O", count());
+ setUndoPosition(position());
+ breakEditBlock();
+ enterInsertMode();
+ beginEditBlock(position());
+ moveToFirstNonBlankOnLine();
+ moveToStartOfLine();
+ insertText(Register("\n"));
+ moveUp();
+ insertAutomaticIndentation(false);
endEditBlock();
} else if (input.isControl('o')) {
if (!m_jumpListUndo.isEmpty()) {
setTargetColumn();
setDotCommand("%1p", count());
finishMovement();
- } else if (isVisualMode() && input.is('r')) {
+ } else if (input.is('r')) {
m_submode = TransformSubMode;
m_subsubmode = ReplaceSubSubMode;
- } else if (input.is('r')) {
- m_submode = ReplaceSubMode;
- setDotCommand(QString(QLatin1Char('r')));
} else if (!isVisualMode() && input.is('R')) {
- // FIXME: right now we repeat the insertion count() times,
- // but not the deletion
- m_lastInsertion.clear();
- enterInsertMode();
- m_submode = ReplaceSubMode;
- setDotCommand(QString(QLatin1Char('R')));
+ setUndoPosition(position());
+ breakEditBlock();
+ enterReplaceMode();
updateMiniBuffer();
} else if (input.isControl('r')) {
redo();
moveLeft();
setAnchor();
moveRight(qMin(count(), rightDist()));
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
setDotCommand("%1s", count());
m_opcount.clear();
m_mvcount.clear();
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
} else if (input.is('S')) {
if (!isVisualMode()) {
setPosition(lastPositionInLine(line + count() - 1));
}
setDotCommand("%1S", count());
+ setUndoPosition(position());
+ breakEditBlock();
enterInsertMode();
- m_beginEditBlock = false;
m_submode = ChangeSubMode;
m_movetype = MoveLineWise;
finishMovement();
if (leftDist() > 0) {
setAnchor();
moveLeft(qMin(count(), leftDist()));
- yankSelectedText();
- removeSelectedText();
+ yankText(currentRange(), m_register);
+ removeText(currentRange());
}
finishMovement();
} else if ((m_submode == YankSubMode && input.is('y'))
|| (input.is('Y') && isVisualLineMode())
|| (input.is('Y') && isVisualCharMode())) {
m_rangemode = RangeLineMode;
- yankSelectedText();
+ yankText(currentRange(), m_register);
setPosition(qMin(position(), anchor()));
moveToStartOfLine();
leaveVisualMode();
finishMovement();
} else if ((input.is('y') || input.is('Y')) && isVisualBlockMode()) {
m_rangemode = RangeBlockMode;
- yankSelectedText();
+ yankText(currentRange(), m_register);
setPosition(qMin(position(), anchor()));
leaveVisualMode();
finishMovement();
} else if (input.is('Z')) {
m_submode = CapitalZSubMode;
} else if (!m_gflag && input.is('~') && !isVisualMode()) {
+ m_movetype = MoveExclusive;
if (!atEndOfLine()) {
beginEditBlock();
setAnchor();
moveRight(qMin(count(), rightDist()));
if (input.is('~')) {
- invertCaseSelectedText();
+ invertCase(currentRange());
setDotCommand("%1~", count());
} else if (input.is('u')) {
- downCaseSelectedText();
+ downCase(currentRange());
setDotCommand("%1gu", count());
} else if (input.is('U')) {
- upCaseSelectedText();
+ upCase(currentRange());
setDotCommand("%1gU", count());
}
endEditBlock();
|| (m_gflag && input.is('u') && !isVisualMode())
|| (m_gflag && input.is('U') && !isVisualMode())) {
m_gflag = false;
+ m_movetype = MoveExclusive;
if (atEndOfLine())
moveLeft();
setAnchor();
|| (m_gflag && input.is('u') && isVisualMode())
|| (m_gflag && input.is('U') && isVisualMode())) {
m_gflag = false;
+ m_movetype = MoveExclusive;
if (isVisualLineMode())
m_rangemode = RangeLineMode;
else if (isVisualBlockMode())
} else if (input.is(Key_Delete)) {
setAnchor();
moveRight(qMin(1, rightDist()));
- removeSelectedText();
+ removeText(currentRange());
} else if (input.is(Key_BracketLeft) || input.is(Key_BracketRight)) {
} else if (input.isControl(Key_BracketRight)) {
return handled;
}
+EventResult FakeVimHandler::Private::handleReplaceMode(const Input &input)
+{
+ if (input.isEscape()) {
+ moveLeft(qMin(1, leftDist()));
+ setTargetColumn();
+ m_submode = NoSubMode;
+ m_mode = CommandMode;
+ finishMovement();
+ } else {
+ joinPreviousEditBlock();
+ if (!atEndOfLine()) {
+ setAnchor();
+ moveRight();
+ m_lastDeletion += selectText(Range(position(), anchor()));
+ removeText(currentRange());
+ }
+ const QString text = input.text();
+ m_lastInsertion += text;
+ insertText(text);
+ endEditBlock();
+ }
+ return EventHandled;
+}
+
EventResult FakeVimHandler::Private::handleInsertMode(const Input &input)
{
//const int key = input.key;
//const QString &text = input.text;
- if (input.isKey(Key_Escape) || input.isKey(27) || input.isControl('c') ||
- input.isControl(Key_BracketLeft)) {
+ if (input.isEscape()) {
if (isVisualBlockMode() && !m_lastInsertion.contains('\n')) {
leaveVisualMode();
joinPreviousEditBlock();
setTargetColumn();
for (int i = 0; i < m_visualInsertCount; ++i) {
moveDown();
- m_tc.insertText(m_lastInsertion);
+ insertText(m_lastInsertion);
}
moveLeft(1);
Range range(pos, position(), RangeBlockMode);
setDotCommand("p");
endEditBlock();
} else {
- // normal insertion. start with '1', as one instance was
- // already physically inserted while typing
- QString data = m_lastInsertion;
- for (int i = 1; i < count(); ++i) {
- m_tc.insertText(m_lastInsertion);
+ // Normal insertion. Start with '1', as one instance was
+ // already physically inserted while typing.
+ QString data;
+ for (int i = 1; i < count(); ++i)
data += m_lastInsertion;
- }
+ insertText(data);
moveLeft(qMin(1, leftDist()));
setTargetColumn();
leaveVisualMode();
- recordNewUndo();
}
- m_dotCommand += m_lastInsertion;
- m_dotCommand += QChar(27);
+ g.dotCommand += m_lastInsertion;
+ g.dotCommand += QChar(27);
enterCommandMode();
m_submode = NoSubMode;
} else if (input.isKey(Key_Insert)) {
- if (m_submode == ReplaceSubMode) {
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(false));
- m_submode = NoSubMode;
- } else {
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(true));
- m_submode = ReplaceSubMode;
- }
+ if (m_mode == ReplaceMode)
+ m_mode = InsertMode;
+ else
+ m_mode = ReplaceMode;
+ updateCursor();
} else if (input.isKey(Key_Left)) {
moveLeft(count());
setTargetColumn();
moveBehindEndOfLine();
setTargetColumn();
m_lastInsertion.clear();
- } else if (input.isKey(Key_Return)) {
+ } else if (input.isReturn()) {
m_submode = NoSubMode;
- m_tc.insertBlock();
+ insertText(Register("\n"));
m_lastInsertion += "\n";
insertAutomaticIndentation(true);
setTargetColumn();
- } else if (input.isKey(Key_Backspace) || input.isControl('h')) {
+ } else if (input.isBackspace()) {
joinPreviousEditBlock();
m_justAutoIndented = 0;
if (!m_lastInsertion.isEmpty() || hasConfig(ConfigBackspace, "start")) {
m_lastInsertion.clear(); // FIXME
} else {
m_tc.deletePreviousChar();
+ fixMarks(position(), -1);
m_lastInsertion.chop(1);
}
setTargetColumn();
const int col = physicalCursorColumnInDocument();
QString str = QString(ts - col % ts, ' ');
m_lastInsertion.append(str);
- m_tc.insertText(str);
+ insertText(str);
setTargetColumn();
} else if (input.isControl('d')) {
// remove one level of indentation from the current line
//} else if (key >= control('a') && key <= control('z')) {
// // ignore these
} else if (!input.text().isEmpty()) {
- const QString text = input.text();
- if (m_beginEditBlock) {
- beginEditBlock();
- m_beginEditBlock = false;
- } else {
- joinPreviousEditBlock();
- }
+ joinPreviousEditBlock();
m_justAutoIndented = 0;
+ const QString text = input.text();
m_lastInsertion.append(text);
- if (m_submode == ReplaceSubMode) {
- if (atEndOfLine())
- m_submode = NoSubMode;
- else
- m_tc.deleteChar();
- }
- m_tc.insertText(text);
+ insertText(text);
if (hasConfig(ConfigSmartIndent) && isElectricCharacter(text.at(0))) {
const QString leftText = m_tc.block().text()
.left(m_tc.position() - 1 - m_tc.block().position());
if (leftText.simplified().isEmpty()) {
- Range range(position(), position());
- range.rangemode = m_rangemode;
+ Range range(position(), position(), m_rangemode);
indentText(range, text.at(0));
}
}
- if (!m_inReplay)
+ if (!g.inReplay)
emit q->completionRequested();
setTargetColumn();
endEditBlock();
return EventHandled;
}
-EventResult FakeVimHandler::Private::handleMiniBufferModes(const Input &input)
+EventResult FakeVimHandler::Private::handleExMode(const Input &input)
{
- if (input.isKey(Key_Escape) || input.isControl('c')
- || input.isControl(Key_BracketLeft)) {
+ if (input.isEscape()) {
m_commandBuffer.clear();
enterCommandMode();
updateMiniBuffer();
- } else if (input.isKey(Key_Backspace)) {
+ } else if (input.isBackspace()) {
if (m_commandBuffer.isEmpty()) {
+ m_commandPrefix.clear();
enterCommandMode();
} else {
m_commandBuffer.chop(1);
if (!m_commandBuffer.isEmpty())
m_commandBuffer.chop(1);
updateMiniBuffer();
- } else if (input.isKey(Key_Return) && m_mode == ExMode) {
+ } else if (input.isReturn()) {
if (!m_commandBuffer.isEmpty()) {
- m_commandHistory.takeLast();
- m_commandHistory.append(m_commandBuffer);
+ //g.commandHistory.takeLast();
+ g.commandHistory.append(m_commandBuffer);
handleExCommand(m_commandBuffer);
- if (m_textedit || m_plaintextedit) {
+ if (m_textedit || m_plaintextedit)
leaveVisualMode();
- }
- }
- } else if (input.isKey(Key_Return) && isSearchMode()
- && !hasConfig(ConfigIncSearch)) {
- if (!m_commandBuffer.isEmpty()) {
- m_searchHistory.takeLast();
- m_searchHistory.append(m_commandBuffer);
- m_lastSearchForward = (m_mode == SearchForwardMode);
- search(lastSearchString(), m_lastSearchForward);
- recordJump();
}
- enterCommandMode();
updateMiniBuffer();
- } else if ((input.isKey(Key_Up) || input.isKey(Key_PageUp)) && isSearchMode()) {
- // FIXME: This and the three cases below are wrong as vim
- // takes only matching entries in the history into account.
- if (m_searchHistoryIndex > 0) {
- --m_searchHistoryIndex;
- showBlackMessage(m_searchHistory.at(m_searchHistoryIndex));
- }
- } else if ((input.isKey(Key_Up) || input.isKey(Key_PageUp)) && m_mode == ExMode) {
- if (m_commandHistoryIndex > 0) {
- --m_commandHistoryIndex;
- showBlackMessage(m_commandHistory.at(m_commandHistoryIndex));
- }
- } else if ((input.isKey(Key_Down) || input.isKey(Key_PageDown)) && isSearchMode()) {
- if (m_searchHistoryIndex < m_searchHistory.size() - 1) {
- ++m_searchHistoryIndex;
- showBlackMessage(m_searchHistory.at(m_searchHistoryIndex));
- }
- } else if ((input.isKey(Key_Down) || input.isKey(Key_PageDown)) && m_mode == ExMode) {
- if (m_commandHistoryIndex < m_commandHistory.size() - 1) {
- ++m_commandHistoryIndex;
- showBlackMessage(m_commandHistory.at(m_commandHistoryIndex));
- }
+ } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
+ g.commandHistory.up();
+ m_commandBuffer = g.commandHistory.current();
+ updateMiniBuffer();
+ } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
+ g.commandHistory.down();
+ m_commandBuffer = g.commandHistory.current();
+ updateMiniBuffer();
} else if (input.isKey(Key_Tab)) {
m_commandBuffer += QChar(9);
updateMiniBuffer();
- } else if (input.isKey(Key_Return) && isSearchMode()
- && hasConfig(ConfigIncSearch)) {
+ } else if (!input.text().isEmpty()) {
+ m_commandBuffer += input.text();
+ updateMiniBuffer();
+ } else {
+ qDebug() << "IGNORED IN EX-MODE: " << input.key() << input.text();
+ return EventUnhandled;
+ }
+ return EventHandled;
+}
+
+EventResult FakeVimHandler::Private::handleSearchSubSubMode(const Input &input)
+{
+ if (input.isEscape()) {
+ m_commandBuffer.clear();
+ m_searchCursor = QTextCursor();
+ updateSelection();
+ enterCommandMode();
+ updateMiniBuffer();
+ } else if (input.isBackspace()) {
+ if (m_commandBuffer.isEmpty()) {
+ m_commandPrefix.clear();
+ m_searchCursor = QTextCursor();
+ enterCommandMode();
+ } else {
+ m_commandBuffer.chop(1);
+ }
+ updateMiniBuffer();
+ } else if (input.isKey(Key_Left)) {
+ if (!m_commandBuffer.isEmpty())
+ m_commandBuffer.chop(1);
+ updateMiniBuffer();
+ } else if (input.isReturn()) {
+ m_searchCursor = QTextCursor();
+ QString needle = m_commandBuffer;
+ if (!needle.isEmpty()) {
+ g.searchHistory.append(needle);
+ if (!hasConfig(ConfigIncSearch)) {
+ SearchData sd;
+ sd.needle = needle;
+ sd.forward = m_lastSearchForward;
+ sd.highlightCursor = false;
+ sd.highlightMatches = true;
+ search(sd);
+ }
+ finishMovement(m_commandPrefix + needle + "\n");
+ }
enterCommandMode();
- QString needle = m_commandBuffer.mid(1); // FIXME: why
highlightMatches(needle);
updateMiniBuffer();
- } else if (isSearchMode() && hasConfig(ConfigIncSearch)) {
- m_commandBuffer = m_commandBuffer.mid(1); // FIXME: why
- QString needle = m_commandBuffer + input.text();
- search(needle, m_lastSearchForward, true);
+ } else if (input.isKey(Key_Up) || input.isKey(Key_PageUp)) {
+ // FIXME: This and the three cases below are wrong as vim
+ // takes only matching entries in the history into account.
+ g.searchHistory.up();
+ showBlackMessage(g.searchHistory.current());
+ } else if (input.isKey(Key_Down) || input.isKey(Key_PageDown)) {
+ g.searchHistory.down();
+ showBlackMessage(g.searchHistory.current());
+ } else if (input.isKey(Key_Tab)) {
+ m_commandBuffer += QChar(9);
updateMiniBuffer();
- recordJump();
} else if (!input.text().isEmpty()) {
m_commandBuffer += input.text();
updateMiniBuffer();
- } else {
- qDebug() << "IGNORED IN MINIBUFFER MODE: " << input.key() << input.text();
- return EventUnhandled;
}
+
+ if (hasConfig(ConfigIncSearch) && !input.isReturn() && !input.isEscape()) {
+ SearchData sd;
+ sd.needle = m_commandBuffer;
+ sd.forward = m_lastSearchForward;
+ sd.mustMove = false;
+ sd.highlightCursor = true;
+ sd.highlightMatches = false;
+ search(sd);
+ }
+
+ //else {
+ // qDebug() << "IGNORED IN SEARCH MODE: " << input.key() << input.text();
+ // return EventUnhandled;
+ //}
return EventHandled;
}
-// 1 based.
+// This uses 1 based line counting.
int FakeVimHandler::Private::readLineCode(QString &cmd)
{
//qDebug() << "CMD: " << cmd;
return -1;
QChar c = cmd.at(0);
cmd = cmd.mid(1);
- if (c == '.')
+ if (c == '.') {
+ if (cmd.isEmpty())
+ return cursorLineInDocument() + 1;
+ QChar c1 = cmd.at(0);
+ if (c1 == '+' || c1 == '-') {
+ // Repeat for things like .+4
+ cmd = cmd.mid(1);
+ return cursorLineInDocument() + readLineCode(cmd);
+ }
return cursorLineInDocument() + 1;
+ }
if (c == '$')
return linesInDocument();
if (c == '\'' && !cmd.isEmpty()) {
- int mark = m_marks.value(cmd.at(0).unicode());
- if (!mark) {
- showRedMessage(msgE20MarkNotSet(cmd.at(0)));
+ if (cmd.isEmpty()) {
+ showRedMessage(msgMarkNotSet(QString()));
+ return -1;
+ }
+ int m = mark(cmd.at(0).unicode());
+ if (m == -1) {
+ showRedMessage(msgMarkNotSet(cmd.at(0)));
cmd = cmd.mid(1);
return -1;
}
cmd = cmd.mid(1);
- return lineForPosition(mark);
+ return lineForPosition(m);
}
if (c == '-') {
int n = readLineCode(cmd);
return cursorLineInDocument() + 1 + (n == -1 ? 1 : n);
}
if (c == '\'' && !cmd.isEmpty()) {
- int pos = m_marks.value(cmd.at(0).unicode(), -1);
- //qDebug() << " MARK: " << cmd.at(0) << pos << lineForPosition(pos);
+ int pos = mark(cmd.at(0).unicode());
if (pos == -1) {
- showRedMessage(msgE20MarkNotSet(cmd.at(0)));
+ showRedMessage(msgMarkNotSet(cmd.at(0)));
cmd = cmd.mid(1);
return -1;
}
//qDebug() << "N: " << n;
return n;
}
- // not parsed
+ // Parsing failed.
cmd = c + cmd;
return -1;
}
-void FakeVimHandler::Private::selectRange(int beginLine, int endLine)
+void FakeVimHandler::Private::setCurrentRange(const Range &range)
{
- if (beginLine == -1)
- beginLine = cursorLineInDocument();
- if (endLine == -1)
- endLine = cursorLineInDocument();
- if (beginLine > endLine)
- qSwap(beginLine, endLine);
- setAnchor(firstPositionInLine(beginLine));
- if (endLine == linesInDocument())
- setPosition(lastPositionInLine(endLine));
- else
- setPosition(firstPositionInLine(endLine + 1));
+ setAnchor(range.beginPos);
+ setPosition(range.endPos);
+ m_rangemode = range.rangemode;
}
// use handleExCommand for invoking commands that might move the cursor
EDITOR(setTextCursor(m_tc));
}
-QString FakeVimHandler::Private::extractCommand(const QString &line,
- int *beginLine, int *endLine)
-{
- QString cmd = line;
- *beginLine = -1;
- *endLine = -1;
-
- // FIXME: that seems to be different for %w and %s
- if (cmd.startsWith(QLatin1Char('%')))
- cmd = "1,$" + cmd.mid(1);
-
- int lineNumber = readLineCode(cmd);
- if (lineNumber != -1)
- *beginLine = lineNumber;
-
- if (cmd.startsWith(',')) {
- cmd = cmd.mid(1);
- lineNumber = readLineCode(cmd);
- if (lineNumber != -1)
- *endLine = lineNumber;
- }
- //qDebug() << "RANGE: " << beginLine << endLine << cmd << lineNumber << m_marks;
- return cmd;
-}
-
-bool FakeVimHandler::Private::handleExSubstituteCommand(const QString &line)
+bool FakeVimHandler::Private::handleExSubstituteCommand(const ExCommand &cmd)
// :substitute
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (cmd.startsWith(QLatin1String("substitute")))
- cmd = cmd.mid(10);
- else if (cmd.startsWith('s') && line.size() > 1
- && !isalpha(cmd.at(1).unicode()))
- cmd = cmd.mid(1);
+ QString line = cmd.cmd + ' ' + cmd.args;
+ line = line.trimmed();
+ if (line.startsWith(_("substitute")))
+ line = line.mid(10);
+ else if (line.startsWith('s') && line.size() > 1
+ && !isalpha(line.at(1).unicode()))
+ line = line.mid(1);
else
return false;
+
// we have /{pattern}/{string}/[flags] now
- if (cmd.isEmpty())
+ if (line.isEmpty())
return false;
- const QChar separator = cmd.at(0);
+ const QChar separator = line.at(0);
int pos1 = -1;
int pos2 = -1;
int i;
- for (i = 1; i < cmd.size(); ++i) {
- if (cmd.at(i) == separator && cmd.at(i - 1) != '\\') {
+ for (i = 1; i < line.size(); ++i) {
+ if (line.at(i) == separator && line.at(i - 1) != '\\') {
pos1 = i;
break;
}
}
if (pos1 == -1)
return false;
- for (++i; i < cmd.size(); ++i) {
- if (cmd.at(i) == separator && cmd.at(i - 1) != '\\') {
+ for (++i; i < line.size(); ++i) {
+ if (line.at(i) == separator && line.at(i - 1) != '\\') {
pos2 = i;
break;
}
}
if (pos2 == -1)
- pos2 = cmd.size();
+ pos2 = line.size();
- QString needle = cmd.mid(1, pos1 - 1);
- const QString replacement = cmd.mid(pos1 + 1, pos2 - pos1 - 1);
- QString flags = cmd.mid(pos2 + 1);
+ QString needle = line.mid(1, pos1 - 1);
+ const QString replacement = line.mid(pos1 + 1, pos2 - pos1 - 1);
+ QString flags = line.mid(pos2 + 1);
needle.replace('$', '\n');
needle.replace("\\\n", "\\$");
if (flags.contains('i'))
pattern.setCaseSensitivity(Qt::CaseInsensitive);
const bool global = flags.contains('g');
+ const int beginLine = lineForPosition(cmd.range.beginPos);
+ const int endLine = lineForPosition(cmd.range.endPos);
beginEditBlock();
for (int line = endLine; line >= beginLine; --line) {
QString origText = lineContents(line);
return true;
}
-bool FakeVimHandler::Private::handleExMapCommand(const QString &line) // :map
+bool FakeVimHandler::Private::handleExMapCommand(const ExCommand &cmd0) // :map
{
- int pos1 = line.indexOf(QLatin1Char(' '));
- if (pos1 == -1)
- return false;
- int pos2 = line.indexOf(QLatin1Char(' '), pos1 + 1);
- if (pos2 == -1)
- return false;
-
QByteArray modes;
enum Type { Map, Noremap, Unmap } type;
- QByteArray cmd = line.left(pos1).toLatin1();
+ QByteArray cmd = cmd0.cmd.toLatin1();
// Strange formatting. But everything else is even uglier.
if (cmd == "map") { modes = "nvo"; type = Map; } else
else
return false;
- QString lhs = line.mid(pos1 + 1, pos2 - pos1 - 1);
- QString rhs = line.mid(pos2 + 1);
+ const int pos = cmd0.args.indexOf(QLatin1Char(' '));
+ if (pos == -1) {
+ // FIXME: Dump mappings here.
+ //qDebug() << g.mappings;
+ return true;;
+ }
+
+ QString lhs = cmd0.args.left(pos);
+ QString rhs = cmd0.args.mid(pos + 1);
Inputs key;
- foreach (QChar c, lhs)
- key.append(Input(c));
+ key.parseFrom(lhs);
//qDebug() << "MAPPING: " << modes << lhs << rhs;
switch (type) {
case Unmap:
foreach (char c, modes)
- if (m_mappings.contains(c))
- m_mappings[c].remove(key);
+ if (g.mappings.contains(c))
+ g.mappings[c].remove(key);
break;
case Map:
rhs = rhs; // FIXME: expand rhs.
// Fall through.
case Noremap: {
- Inputs inputs;
- foreach (QChar c, rhs)
- inputs.append(Input(c));
+ Inputs inputs(rhs);
foreach (char c, modes)
- m_mappings[c].insert(key, inputs);
+ g.mappings[c].insert(key, inputs);
break;
}
}
return true;
}
-bool FakeVimHandler::Private::handleExHistoryCommand(const QString &cmd) // :history
+bool FakeVimHandler::Private::handleExHistoryCommand(const ExCommand &cmd)
{
- static QRegExp reHistory("^his(tory)?( (.*))?$");
- if (reHistory.indexIn(cmd) == -1)
+ // :history
+ if (cmd.cmd != "his" && cmd.cmd != "history")
return false;
- QString arg = reHistory.cap(3);
- if (arg.isEmpty()) {
+ if (cmd.args.isEmpty()) {
QString info;
info += "# command history\n";
int i = 0;
- foreach (const QString &item, m_commandHistory) {
+ foreach (const QString &item, g.commandHistory.items()) {
++i;
info += QString("%1 %2\n").arg(i, -8).arg(item);
}
return true;
}
-bool FakeVimHandler::Private::handleExSetCommand(const QString &cmd) // :set
+bool FakeVimHandler::Private::handleExRegisterCommand(const ExCommand &cmd)
+{
+ // :reg and :di[splay]
+ if (cmd.cmd != "reg" && cmd.cmd != "registers"
+ && cmd.cmd != "di" && cmd.cmd != "display")
+ return false;
+
+ QByteArray regs = cmd.args.toLatin1();
+ if (regs.isEmpty()) {
+ regs = "\"0123456789";
+ QHashIterator<int, Register> it(g.registers);
+ while (it.hasNext()) {
+ it.next();
+ if (it.key() > '9')
+ regs += char(it.key());
+ }
+ }
+ QString info;
+ info += "--- Registers ---\n";
+ foreach (char reg, regs) {
+ QString value = quoteUnprintable(g.registers[reg].contents);
+ info += QString("\"%1 %2\n").arg(reg).arg(value);
+ }
+ emit q->extraInformationChanged(info);
+ updateMiniBuffer();
+ return true;
+}
+
+bool FakeVimHandler::Private::handleExSetCommand(const ExCommand &cmd)
{
- static QRegExp reSet("^set?( (.*))?$");
- if (reSet.indexIn(cmd) == -1)
+ // :set
+ if (cmd.cmd != "se" && cmd.cmd != "set")
return false;
showBlackMessage(QString());
- QString arg = reSet.cap(2);
- SavedAction *act = theFakeVimSettings()->item(arg);
- if (arg.isEmpty()) {
- theFakeVimSetting(SettingsDialog)->trigger(QVariant());
- } else if (act && act->value().type() == QVariant::Bool) {
- // boolean config to be switched on
+ SavedAction *act = theFakeVimSettings()->item(cmd.args);
+ QTC_ASSERT(!cmd.args.isEmpty(), /**/); // Handled by plugin.
+ if (act && act->value().type() == QVariant::Bool) {
+ // Boolean config to be switched on.
bool oldValue = act->value().toBool();
if (oldValue == false)
act->setValue(true);
else if (oldValue == true)
{} // nothing to do
} else if (act) {
- // non-boolean to show
- showBlackMessage(arg + '=' + act->value().toString());
- } else if (arg.startsWith(QLatin1String("no"))
- && (act = theFakeVimSettings()->item(arg.mid(2)))) {
- // boolean config to be switched off
+ // Non-boolean to show.
+ showBlackMessage(cmd.args + '=' + act->value().toString());
+ } else if (cmd.args.startsWith(_("no"))
+ && (act = theFakeVimSettings()->item(cmd.args.mid(2)))) {
+ // Boolean config to be switched off.
bool oldValue = act->value().toBool();
if (oldValue == true)
act->setValue(false);
else if (oldValue == false)
{} // nothing to do
- } else if (arg.contains('=')) {
- // non-boolean config to set
- int p = arg.indexOf('=');
- act = theFakeVimSettings()->item(arg.left(p));
+ } else if (cmd.args.contains('=')) {
+ // Non-boolean config to set.
+ int p = cmd.args.indexOf('=');
+ act = theFakeVimSettings()->item(cmd.args.left(p));
if (act)
- act->setValue(arg.mid(p + 1));
+ act->setValue(cmd.args.mid(p + 1));
} else {
- passUnknownSetCommand(arg);
+ showRedMessage(FakeVimHandler::tr("Unknown option: ") + cmd.args);
}
updateMiniBuffer();
updateEditor();
return true;
}
-bool FakeVimHandler::Private::handleExNormalCommand(const QString &cmd) // :normal
+bool FakeVimHandler::Private::handleExNormalCommand(const ExCommand &cmd)
{
- static QRegExp reNormal("^norm(al)?( (.*))?$");
- if (reNormal.indexIn(cmd) == -1)
+ // :normal
+ if (cmd.cmd != "norm" && cmd.cmd != "normal")
return false;
- //qDebug() << "REPLAY: " << reNormal.cap(3);
- replay(reNormal.cap(3), 1);
+ //qDebug() << "REPLAY NORMAL: " << quoteUnprintable(reNormal.cap(3));
+ replay(cmd.args, 1);
return true;
}
-bool FakeVimHandler::Private::handleExDeleteCommand(const QString &line) // :d
+bool FakeVimHandler::Private::handleExDeleteCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- static QRegExp reDelete("^d( (.*))?$");
- if (reDelete.indexIn(cmd) != -1)
+ // :delete
+ if (cmd.cmd != "d" && cmd.cmd != "delete")
return false;
- selectRange(beginLine, endLine);
- QString reg = reDelete.cap(2);
- QString text = selectedText();
- removeSelectedText();
+ setCurrentRange(cmd.range);
+ QString reg = cmd.args;
+ QString text = selectText(cmd.range);
+ removeText(currentRange());
if (!reg.isEmpty()) {
- Register &r = m_registers[reg.at(0).unicode()];
+ Register &r = g.registers[reg.at(0).unicode()];
r.contents = text;
r.rangemode = RangeLineMode;
}
return true;
}
-bool FakeVimHandler::Private::handleExWriteCommand(const QString &line)
- // :w, :x, :q, :wq, ...
+bool FakeVimHandler::Private::handleExWriteCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
- if (reWrite.indexIn(cmd) == -1) // :w and :x
+ // :w, :x, :wq, ...
+ //static QRegExp reWrite("^[wx]q?a?!?( (.*))?$");
+ if (cmd.cmd != "w" && cmd.cmd != "x" && cmd.cmd != "wq")
return false;
- bool noArgs = (beginLine == -1);
+ int beginLine = lineForPosition(cmd.range.beginPos);
+ int endLine = lineForPosition(cmd.range.endPos);
+ const bool noArgs = (beginLine == -1);
if (beginLine == -1)
beginLine = 0;
if (endLine == -1)
endLine = linesInDocument();
//qDebug() << "LINES: " << beginLine << endLine;
- int indexOfSpace = cmd.indexOf(QChar(' '));
- QString prefix;
- if (indexOfSpace < 0)
- prefix = cmd;
- else
- prefix = cmd.left(indexOfSpace);
- bool forced = prefix.contains(QChar('!'));
- bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
- bool quitAll = quit && prefix.contains(QChar('a'));
- QString fileName = reWrite.cap(2);
+ QString prefix = cmd.args;
+ const bool forced = cmd.hasBang;
+ //const bool quit = prefix.contains(QChar('q')) || prefix.contains(QChar('x'));
+ //const bool quitAll = quit && prefix.contains(QChar('a'));
+ QString fileName = cmd.args;
if (fileName.isEmpty())
fileName = m_currentFileName;
QFile file1(fileName);
- bool exists = file1.exists();
+ const bool exists = file1.exists();
if (exists && !forced && !noArgs) {
showRedMessage(FakeVimHandler::tr
- ("File '%1' exists (add ! to override)").arg(fileName));
+ ("File \"%1\" exists (add ! to override)").arg(fileName));
} else if (file1.open(QIODevice::ReadWrite)) {
+ // Nobody cared, so act ourselves.
file1.close();
QTextCursor tc = m_tc;
Range range(firstPositionInLine(beginLine),
firstPositionInLine(endLine), RangeLineMode);
- QString contents = text(range);
+ QString contents = selectText(range);
m_tc = tc;
- //qDebug() << "LINES: " << beginLine << endLine;
- bool handled = false;
- emit q->writeFileRequested(&handled, fileName, contents);
- // nobody cared, so act ourselves
- if (!handled) {
- //qDebug() << "HANDLING MANUAL SAVE TO " << fileName;
- QFile::remove(fileName);
- QFile file2(fileName);
- if (file2.open(QIODevice::ReadWrite)) {
- QTextStream ts(&file2);
- ts << contents;
- } else {
- showRedMessage(FakeVimHandler::tr
- ("Cannot open file '%1' for writing").arg(fileName));
- }
+ QFile::remove(fileName);
+ QFile file2(fileName);
+ if (file2.open(QIODevice::ReadWrite)) {
+ QTextStream ts(&file2);
+ ts << contents;
+ } else {
+ showRedMessage(FakeVimHandler::tr
+ ("Cannot open file \"%1\" for writing").arg(fileName));
}
- // check result by reading back
+ // Check result by reading back.
QFile file3(fileName);
file3.open(QIODevice::ReadOnly);
QByteArray ba = file3.readAll();
showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
.arg(fileName).arg(exists ? " " : " [New] ")
.arg(ba.count('\n')).arg(ba.size()));
- if (quitAll)
- passUnknownExCommand(forced ? "qa!" : "qa");
- else if (quit)
- passUnknownExCommand(forced ? "q!" : "q");
+ //if (quitAll)
+ // passUnknownExCommand(forced ? "qa!" : "qa");
+ //else if (quit)
+ // passUnknownExCommand(forced ? "q!" : "q");
} else {
showRedMessage(FakeVimHandler::tr
- ("Cannot open file '%1' for reading").arg(fileName));
+ ("Cannot open file \"%1\" for reading").arg(fileName));
}
return true;
}
-bool FakeVimHandler::Private::handleExReadCommand(const QString &line) // :r
+bool FakeVimHandler::Private::handleExReadCommand(const ExCommand &cmd)
{
- if (!line.startsWith(QLatin1String("r ")))
+ // :read
+ if (cmd.cmd != "r" && cmd.cmd != "read")
return false;
beginEditBlock();
moveToStartOfLine();
setTargetColumn();
moveDown();
- m_currentFileName = line.mid(2);
+ m_currentFileName = cmd.args;
QFile file(m_currentFileName);
file.open(QIODevice::ReadOnly);
QTextStream ts(&file);
return true;
}
-bool FakeVimHandler::Private::handleExBangCommand(const QString &line) // :!
+bool FakeVimHandler::Private::handleExBangCommand(const ExCommand &cmd) // :!
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (!cmd.startsWith(QLatin1Char('!')))
+ if (!cmd.cmd.startsWith(QLatin1Char('!')))
return false;
- selectRange(beginLine, endLine);
- int targetPosition = firstPositionInLine(beginLine);
- QString command = cmd.mid(1).trimmed();
- QString text = selectedText();
+ setCurrentRange(cmd.range);
+ int targetPosition = firstPositionInLine(lineForPosition(cmd.range.beginPos));
+ QString command = QString(cmd.cmd.mid(1) + ' ' + cmd.args).trimmed();
+ QString text = selectText(cmd.range);
QProcess proc;
- proc.start(cmd.mid(1));
+ proc.start(command);
proc.waitForStarted();
+#ifdef Q_OS_WIN
+ text.replace(_("\n"), _("\r\n"));
+#endif
proc.write(text.toUtf8());
proc.closeWriteChannel();
proc.waitForFinished();
QString result = QString::fromUtf8(proc.readAllStandardOutput());
beginEditBlock(targetPosition);
- removeSelectedText();
- m_tc.insertText(result);
+ removeText(currentRange());
+ insertText(result);
setPosition(targetPosition);
endEditBlock();
leaveVisualMode();
return true;
}
-bool FakeVimHandler::Private::handleExShiftRightCommand(const QString &line) // :>
+bool FakeVimHandler::Private::handleExShiftCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (!cmd.startsWith(QLatin1Char('>')))
+ if (cmd.cmd != "<" && cmd.cmd != ">")
return false;
- m_anchor = firstPositionInLine(beginLine);
- setPosition(firstPositionInLine(endLine));
- shiftRegionRight(1);
+ setCurrentRange(cmd.range);
+ int count = qMin(1, cmd.args.toInt());
+ if (cmd.cmd == "<")
+ shiftRegionLeft(count);
+ else
+ shiftRegionRight(count);
leaveVisualMode();
- showBlackMessage(FakeVimHandler::tr("%n lines >ed %1 time", 0,
- (endLine - beginLine + 1)).arg(1));
+ const int beginLine = lineForPosition(cmd.range.beginPos);
+ const int endLine = lineForPosition(cmd.range.endPos);
+ showBlackMessage(FakeVimHandler::tr("%n lines %1ed %2 time", 0,
+ (endLine - beginLine + 1)).arg(cmd.cmd).arg(count));
return true;
}
-bool FakeVimHandler::Private::handleExRedoCommand(const QString &line) // :redo
+bool FakeVimHandler::Private::handleExRedoCommand(const ExCommand &cmd)
{
- if (line != "red" && line != "redo")
+ // :redo
+ if (cmd.cmd != "red" && cmd.cmd != "redo")
return false;
redo();
return true;
}
-bool FakeVimHandler::Private::handleExGotoCommand(const QString &line) // :<nr>
+bool FakeVimHandler::Private::handleExGotoCommand(const ExCommand &cmd)
{
- int beginLine, endLine;
- QString cmd = extractCommand(line, &beginLine, &endLine);
-
- if (!cmd.isEmpty())
+ // :<nr>
+ if (!cmd.cmd.isEmpty())
return false;
+ const int beginLine = lineForPosition(cmd.range.beginPos);
setPosition(firstPositionInLine(beginLine));
showBlackMessage(QString());
return true;
}
-bool FakeVimHandler::Private::handleExSourceCommand(const QString &line) // :source
+bool FakeVimHandler::Private::handleExSourceCommand(const ExCommand &cmd)
{
- int pos = line.indexOf(' ');
- if (line.leftRef(pos) != "so" && line.leftRef(pos) != "source")
+ // :source
+ if (cmd.cmd != "so" && cmd.cmd != "source")
return false;
- QString fileName = line.mid(pos + 1);
+ QString fileName = cmd.args;
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
showRedMessage(FakeVimHandler::tr("Can't open file %1").arg(fileName));
void FakeVimHandler::Private::handleExCommand(const QString &line0)
{
QString line = line0; // Make sure we have a copy to prevent aliasing.
+ // FIXME: that seems to be different for %w and %s
+ if (line.startsWith(QLatin1Char('%')))
+ line = "1,$" + line.mid(1);
+
+ int beginLine = readLineCode(line);
+ int endLine = -1;
+ if (line.startsWith(',')) {
+ line = line.mid(1);
+ endLine = readLineCode(line);
+ }
+ if (beginLine != -1 && endLine == -1)
+ endLine = beginLine;
+ const int beginPos = firstPositionInLine(beginLine);
+ const int endPos = lastPositionInLine(endLine);
+ ExCommand cmd;
+ const QString arg0 = line.section(' ', 0, 0);
+ cmd.cmd = arg0;
+ cmd.args = line.mid(arg0.size() + 1).trimmed();
+ cmd.range = Range(beginPos, endPos, RangeLineMode);
+ cmd.hasBang = arg0.endsWith('!');
+ if (cmd.hasBang)
+ cmd.cmd.chop(1);
+ //qDebug() << "CMD: " << cmd;
+
enterCommandMode();
showBlackMessage(QString());
- if (handleExCommandHelper(line))
- return;
- int beginLine, endLine;
- passUnknownExCommand(extractCommand(line, &beginLine, &endLine));
+ if (!handleExCommandHelper(cmd))
+ showRedMessage(tr("Not an editor command: %1").arg(cmd.cmd));
}
-bool FakeVimHandler::Private::handleExCommandHelper(const QString &line)
+bool FakeVimHandler::Private::handleExCommandHelper(const ExCommand &cmd)
{
- return handleExGotoCommand(line)
- || handleExBangCommand(line)
- || handleExHistoryCommand(line)
- || handleExMapCommand(line)
- || handleExNormalCommand(line)
- || handleExReadCommand(line)
- || handleExRedoCommand(line)
- || handleExSetCommand(line)
- || handleExShiftRightCommand(line)
- || handleExSourceCommand(line)
- || handleExSubstituteCommand(line)
- || handleExWriteCommand(line);
+ return handleExPluginCommand(cmd)
+ || handleExGotoCommand(cmd)
+ || handleExBangCommand(cmd)
+ || handleExHistoryCommand(cmd)
+ || handleExRegisterCommand(cmd)
+ || handleExDeleteCommand(cmd)
+ || handleExMapCommand(cmd)
+ || handleExNormalCommand(cmd)
+ || handleExReadCommand(cmd)
+ || handleExRedoCommand(cmd)
+ || handleExSetCommand(cmd)
+ || handleExShiftCommand(cmd)
+ || handleExSourceCommand(cmd)
+ || handleExSubstituteCommand(cmd)
+ || handleExWriteCommand(cmd);
}
-void FakeVimHandler::Private::passUnknownExCommand(const QString &cmd)
+bool FakeVimHandler::Private::handleExPluginCommand(const ExCommand &cmd)
{
EDITOR(setTextCursor(m_tc));
- emit q->handleExCommandRequested(cmd);
+ bool handled = false;
+ emit q->handleExCommandRequested(&handled, cmd);
if (m_plaintextedit || m_textedit)
m_tc = EDITOR(textCursor());
-}
-
-void FakeVimHandler::Private::passUnknownSetCommand(const QString &arg)
-{
- bool handled = false;
- emit q->handleSetCommandRequested(&handled, arg);
- if (!handled) {
- showRedMessage(FakeVimHandler::tr("E512: Unknown option: ") + arg);
- }
+ //qDebug() << "HANDLER REQUEST: " << cmd.cmd << handled;
+ return handled;
}
static void vimPatternToQtPattern(QString *needle, QTextDocument::FindFlags *flags)
{
// FIXME: Rough mapping of a common case
- if (needle->startsWith(QLatin1String("\\<")) && needle->endsWith(QLatin1String("\\>")))
+ if (needle->startsWith(_("\\<")) && needle->endsWith(_("\\>")))
(*flags) |= QTextDocument::FindWholeWords;
- needle->remove(QLatin1String("\\<")); // start of word
- needle->remove(QLatin1String("\\>")); // end of word
+ needle->remove(_("\\<")); // start of word
+ needle->remove(_("\\>")); // end of word
//qDebug() << "NEEDLE " << needle0 << needle;
}
-void FakeVimHandler::Private::search(const QString &needle0, bool forward,
- bool incSearch)
+void FakeVimHandler::Private::search(const SearchData &sd)
{
- showBlackMessage((forward ? '/' : '?') + needle0);
- CursorPosition origPosition = cursorPosition();
+ if (sd.needle.isEmpty())
+ return;
+
+ const bool incSearch = hasConfig(ConfigIncSearch);
QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
- if (!forward)
+ if (!sd.forward)
flags |= QTextDocument::FindBackward;
- QString needle = needle0;
+ QString needle = sd.needle;
vimPatternToQtPattern(&needle, &flags);
- if (forward)
- m_tc.movePosition(Right, MoveAnchor, 1);
+ const int oldLine = cursorLineInDocument() - cursorLineOnScreen();
- int oldLine = cursorLineInDocument() - cursorLineOnScreen();
+ int startPos = position();
+ if (sd.mustMove)
+ sd.forward ? ++startPos : --startPos;
- EDITOR(setTextCursor(m_tc));
- if (EDITOR(find(needle, flags))) {
- m_tc = EDITOR(textCursor());
- m_tc.setPosition(m_tc.anchor());
- // making this unconditional feels better, but is not "vim like"
- if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
- scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
- if (!incSearch)
- highlightMatches(needle);
- } else {
- m_tc.setPosition(forward ? 0 : lastPositionInDocument());
- EDITOR(setTextCursor(m_tc));
- if (EDITOR(find(needle, flags))) {
- m_tc = EDITOR(textCursor());
- m_tc.setPosition(m_tc.anchor());
- if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
- scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
+ m_searchCursor = QTextCursor();
+ QTextCursor tc = m_tc.document()->find(needle, startPos, flags);
+ if (tc.isNull()) {
+ int startPos = sd.forward ? 0 : lastPositionInDocument();
+ tc = m_tc.document()->find(needle, startPos, flags);
+ if (tc.isNull()) {
if (!incSearch) {
- if (forward)
- showRedMessage(FakeVimHandler::tr("search hit BOTTOM, continuing at TOP"));
- else
- showRedMessage(FakeVimHandler::tr("search hit TOP, continuing at BOTTOM"));
- highlightMatches(needle);
- }
- } else {
- if (!incSearch)
highlightMatches(QString());
- setCursorPosition(origPosition);
- if (!incSearch)
showRedMessage(FakeVimHandler::tr("Pattern not found: ") + needle);
+ }
+ updateSelection();
+ return;
+ }
+ if (!incSearch) {
+ QString msg = sd.forward
+ ? FakeVimHandler::tr("search hit BOTTOM, continuing at TOP")
+ : FakeVimHandler::tr("search hit TOP, continuing at BOTTOM");
+ showRedMessage(msg);
}
}
- if (incSearch) {
- QTextEdit::ExtraSelection sel;
- sel.cursor = m_tc;
- sel.format = m_tc.blockCharFormat();
- sel.format.setForeground(Qt::white);
- sel.format.setBackground(Qt::black);
- sel.cursor.setPosition(m_tc.position(), MoveAnchor);
- sel.cursor.setPosition(m_tc.position() + needle.size(), KeepAnchor);
- QList<QTextEdit::ExtraSelection> selections = m_searchSelections;
- selections.append(sel);
- emit q->selectionChanged(selections);
+
+ // Set Cursor.
+ tc.setPosition(qMin(tc.position(), tc.anchor()), MoveAnchor);
+ tc.clearSelection();
+ m_tc = tc;
+ EDITOR(setTextCursor(m_tc));
+
+ // Making this unconditional feels better, but is not "vim like".
+ if (oldLine != cursorLineInDocument() - cursorLineOnScreen())
+ scrollToLineInDocument(cursorLineInDocument() - linesOnScreen() / 2);
+
+ if (incSearch && sd.highlightCursor) {
+ m_searchCursor = m_tc;
+ m_searchCursor.setPosition(m_tc.position(), MoveAnchor);
+ m_searchCursor.setPosition(m_tc.position() + needle.size(), KeepAnchor);
}
setTargetColumn();
+
+ if (sd.highlightMatches)
+ highlightMatches(needle);
+ updateSelection();
+ recordJump();
}
void FakeVimHandler::Private::highlightMatches(const QString &needle0)
return;
m_oldNeedle = needle0;
m_searchSelections.clear();
-
if (!needle0.isEmpty()) {
QTextCursor tc = m_tc;
tc.movePosition(StartOfDocument, MoveAnchor);
QTextDocument::FindFlags flags = QTextDocument::FindCaseSensitively;
QString needle = needle0;
vimPatternToQtPattern(&needle, &flags);
-
-
- EDITOR(setTextCursor(tc));
- while (EDITOR(find(needle, flags))) {
- tc = EDITOR(textCursor());
+ while (1) {
+ tc = tc.document()->find(needle, tc.position(), flags);
+ if (tc.isNull())
+ break;
QTextEdit::ExtraSelection sel;
sel.cursor = tc;
sel.format = tc.blockCharFormat();
sel.format.setBackground(QColor(177, 177, 0));
m_searchSelections.append(sel);
tc.movePosition(Right, MoveAnchor);
- EDITOR(setTextCursor(tc));
}
}
updateSelection();
int beginLine = qMin(lineForPosition(position()), lineForPosition(anchor()));
int endLine = qMax(lineForPosition(position()), lineForPosition(anchor()));
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
+ Range range(anchor(), position(), m_rangemode);
indentText(range, typedChar);
setPosition(firstPositionInLine(beginLine));
int amount = 0;
// lineForPosition has returned 1-based line numbers
- emit q->indentRegion(&amount, beginLine-1, endLine-1, typedChar);
-
+ emit q->indentRegion(&amount, beginLine - 1, endLine - 1, typedChar);
+ fixMarks(firstPositionInLine(beginLine), amount);
+ if (beginLine != endLine)
+ showBlackMessage("MARKS ARE OFF NOW");
return amount;
}
int targetPos = anchor();
if (beginLine > endLine) {
qSwap(beginLine, endLine);
- targetPos = position();
+ targetPos = position();
}
const int shift = config(ConfigShiftWidth).toInt() * repeat;
const int tab = config(ConfigTabStop).toInt();
return block.position() + block.length() - 1;
}
-QString FakeVimHandler::Private::lastSearchString() const
-{
- return m_searchHistory.empty() ? QString() : m_searchHistory.back();
-}
-
-QString FakeVimHandler::Private::text(const Range &range) const
+QString FakeVimHandler::Private::selectText(const Range &range) const
{
if (range.rangemode == RangeCharMode) {
QTextCursor tc = m_tc;
return contents;
}
-void FakeVimHandler::Private::yankSelectedText()
-{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
- yankText(range, m_register);
-}
-
void FakeVimHandler::Private::yankText(const Range &range, int toregister)
{
- Register ® = m_registers[toregister];
- reg.contents = text(range);
+ Register ® = g.registers[toregister];
+ reg.contents = selectText(range);
reg.rangemode = range.rangemode;
//qDebug() << "YANKED: " << reg.contents;
}
void FakeVimHandler::Private::transformText(const Range &range,
- Transformation transformFunc)
+ Transformation transformFunc, const QVariant &extra)
{
QTextCursor tc = m_tc;
switch (range.rangemode) {
case RangeCharMode: {
tc.setPosition(range.beginPos, MoveAnchor);
tc.setPosition(range.endPos, KeepAnchor);
- (this->*transformFunc)(range.beginPos, &tc);
+ TransformationData td(tc.selectedText(), extra);
+ (this->*transformFunc)(&td);
+ tc.removeSelectedText();
+ fixMarks(range.beginPos, td.to.size() - td.from.size());
+ tc.insertText(td.to);
return;
}
case RangeLineMode:
tc.movePosition(Right, KeepAnchor, 1);
}
}
- (this->*transformFunc)(range.beginPos, &tc);
+ TransformationData td(tc.selectedText(), extra);
+ (this->*transformFunc)(&td);
+ tc.removeSelectedText();
+ fixMarks(range.beginPos, td.to.size() - td.from.size());
+ tc.insertText(td.to);
return;
}
case RangeBlockAndTailMode:
int eCol = qMin(endColumn + 1, block.length() - 1);
tc.setPosition(block.position() + bCol, MoveAnchor);
tc.setPosition(block.position() + eCol, KeepAnchor);
- (this->*transformFunc)(block.position() + bCol, &tc);
+ TransformationData td(tc.selectedText(), extra);
+ (this->*transformFunc)(&td);
+ tc.removeSelectedText();
+ fixMarks(block.position() + bCol, td.to.size() - td.from.size());
+ tc.insertText(td.to);
block = block.previous();
}
endEditBlock();
}
}
-void FakeVimHandler::Private::removeSelectedText()
+void FakeVimHandler::Private::insertText(const Register ®)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
- removeText(range);
+ QTC_ASSERT(reg.rangemode == RangeCharMode,
+ qDebug() << "WRONG INSERT MODE: " << reg.rangemode; return);
+ fixMarks(position(), reg.contents.length());
+ m_tc.insertText(reg.contents);
}
void FakeVimHandler::Private::removeText(const Range &range)
transformText(range, &FakeVimHandler::Private::removeTransform);
}
-void FakeVimHandler::Private::removeTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::removeTransform(TransformationData *td)
{
- fixMarks(updateMarksAfter, tc->selectionStart() - tc->selectionEnd());
- tc->removeSelectedText();
+ Q_UNUSED(td);
}
-void FakeVimHandler::Private::downCaseSelectedText()
+void FakeVimHandler::Private::downCase(const Range &range)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
transformText(range, &FakeVimHandler::Private::downCaseTransform);
}
-void FakeVimHandler::Private::downCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::downCaseTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = c.toLower();
- }
- tc->insertText(str);
+ td->to = td->from.toLower();
}
-void FakeVimHandler::Private::upCaseSelectedText()
+void FakeVimHandler::Private::upCase(const Range &range)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
transformText(range, &FakeVimHandler::Private::upCaseTransform);
}
-void FakeVimHandler::Private::upCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::upCaseTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = c.toUpper();
- }
- tc->insertText(str);
+ td->to = td->from.toUpper();
}
-void FakeVimHandler::Private::invertCaseSelectedText()
+void FakeVimHandler::Private::invertCase(const Range &range)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
transformText(range, &FakeVimHandler::Private::invertCaseTransform);
}
-void FakeVimHandler::Private::invertCaseTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::invertCaseTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = c.isUpper() ? c.toLower() : c.toUpper();
- }
- tc->insertText(str);
+ foreach (QChar c, td->from)
+ td->to += c.isUpper() ? c.toLower() : c.toUpper();
}
-void FakeVimHandler::Private::replaceSelectedText()
+void FakeVimHandler::Private::replaceText(const Range &range, const QString &str)
{
- Range range(anchor(), position());
- range.rangemode = m_rangemode;
- transformText(range, &FakeVimHandler::Private::replaceTransform);
+ transformText(range, &FakeVimHandler::Private::replaceTransform, str);
}
-void FakeVimHandler::Private::replaceTransform(int updateMarksAfter, QTextCursor *tc)
+void FakeVimHandler::Private::replaceTransform(TransformationData *td)
{
- Q_UNUSED(updateMarksAfter);
- QString str = tc->selectedText();
- tc->removeSelectedText();
- for (int i = str.size(); --i >= 0; ) {
- QChar c = str.at(i);
- str[i] = (c.toAscii() == '\n' || c.toAscii() == '\0') ? QChar('\n') : m_replacingCharacter;
+ for (int i = td->from.size(); --i >= 0; ) {
+ QChar c = td->from.at(i);
+ if (c.unicode() == '\n' || c.unicode() == '\0')
+ td->to += QLatin1Char('\n');
+ else
+ td->to += td->extraData.toString();
}
- tc->insertText(str);
}
void FakeVimHandler::Private::pasteText(bool afterCursor)
{
- const QString text = m_registers[m_register].contents;
+ const QString text = g.registers[m_register].contents;
const QStringList lines = text.split(QChar('\n'));
- switch (m_registers[m_register].rangemode) {
+ switch (g.registers[m_register].rangemode) {
case RangeCharMode: {
m_targetColumn = 0;
for (int i = count(); --i >= 0; ) {
if (afterCursor && rightDist() > 0)
moveRight();
- fixMarks(position(), text.length());
- m_tc.insertText(text);
+ insertText(text);
if (!afterCursor && atEndOfLine())
moveLeft();
moveLeft();
for (int i = count(); --i >= 0; ) {
if (afterCursor)
moveDown();
- fixMarks(position(), text.length());
- m_tc.insertText(text);
+ insertText(text);
moveUp(lines.size() - 1);
}
moveToFirstNonBlankOnLine();
} else {
tc.movePosition(Right, MoveAnchor, col - 1 + afterCursor);
}
- qDebug() << "INSERT " << line << " AT " << tc.position()
- << "COL: " << col;
+ //qDebug() << "INSERT " << line << " AT " << tc.position()
+ // << "COL: " << col;
fixMarks(position(), line.length());
tc.insertText(line);
tc.movePosition(StartOfLine, MoveAnchor);
}
//FIXME: This needs to called after undo/insert
-void FakeVimHandler::Private::fixMarks(int positionAction, int positionChange)
-{
- QHashIterator<int, int> i(m_marks);
- while (i.hasNext()) {
- i.next();
- if (i.value() >= positionAction) {
- if (i.value() + positionChange > 0)
- m_marks[i.key()] = i.value() + positionChange;
- else
- m_marks.remove(i.key());
+// The position 'from' is the cursor position after the change. If 'delta'
+// is positive there was a string of size 'delta' inserted after 'from'
+// and consequently all marks beyond 'from + delta' need to be incremented
+// by 'delta'. If text was removed, 'delta' is negative. All marks between
+// 'from' and 'from - delta' need to be removed, everything behing
+// 'from - delta' adjusted by 'delta'.
+void FakeVimHandler::Private::fixMarks(int from, int delta)
+{
+ //qDebug() << "ADJUSTING MARKS FROM " << from << " BY " << delta;
+ if (delta == 0)
+ return;
+ QHashIterator<int, int> it(m_marks);
+ while (it.hasNext()) {
+ it.next();
+ int pos = it.value();
+ if (delta > 0) {
+ // Inserted text.
+ if (pos >= from) {
+ //qDebug() << "MODIFIED: " << it.key() << pos;
+ setMark(it.key(), pos + delta);
+ }
+ } else {
+ // Removed text.
+ if (pos < from) {
+ // Nothing to do.
+ } else if (pos < from - delta) {
+ //qDebug() << "GONE: " << it.key();
+ m_marks.remove(it.key());
+ } else {
+ //qDebug() << "MODIFIED: " << it.key() << pos;
+ setMark(it.key(), pos + delta);
+ }
}
}
}
return m_tc.document()->findBlockByNumber(line - 1).text();
}
-void FakeVimHandler::Private::setLineContents(int line, const QString &contents) const
+void FakeVimHandler::Private::setLineContents(int line, const QString &contents)
{
QTextBlock block = m_tc.document()->findBlockByNumber(line - 1);
QTextCursor tc = m_tc;
tc.setPosition(block.position());
tc.setPosition(block.position() + block.length() - 1, KeepAnchor);
tc.removeSelectedText();
+ fixMarks(block.position(), block.length() - contents.size());
tc.insertText(contents);
}
setAnchor();
m_positionPastEnd = m_anchorPastEnd = false;
m_visualMode = visualMode;
- m_marks['<'] = m_tc.position();
- m_marks['>'] = m_tc.position();
+ setMark('<', m_tc.position());
+ setMark('>', m_tc.position());
updateMiniBuffer();
updateSelection();
}
void FakeVimHandler::Private::undo()
{
//qDebug() << " CURSOR POS: " << m_undoCursorPosition;
- int current = m_tc.document()->availableUndoSteps();
- //endEditBlock();
+ QTextDocument *doc = m_tc.document();
+ // FIXME: That's only an approximaxtion. The real solution might
+ // be to store marks and old userData with QTextBlock setUserData
+ // and retrieve them afterward.
+ const int current = doc->availableUndoSteps();
+ const int oldCount = doc->characterCount();
EDITOR(undo());
- //beginEditBlock();
- int rev = m_tc.document()->availableUndoSteps();
+ const int delta = doc->characterCount() - oldCount;
+ fixMarks(position(), delta);
+ const int rev = doc->availableUndoSteps();
if (current == rev)
showBlackMessage(FakeVimHandler::tr("Already at oldest change"));
else
if (m_undoCursorPosition.contains(rev))
m_tc.setPosition(m_undoCursorPosition[rev]);
+ setTargetColumn();
if (atEndOfLine())
moveLeft();
}
void FakeVimHandler::Private::redo()
{
- int current = m_tc.document()->availableUndoSteps();
- //endEditBlock();
+ QTextDocument *doc = m_tc.document();
+ const int current = m_tc.document()->availableUndoSteps();
+ const int oldCount = doc->characterCount();
EDITOR(redo());
- //beginEditBlock();
- int rev = m_tc.document()->availableUndoSteps();
+ const int delta = doc->characterCount() - oldCount;
+ fixMarks(position(), delta);
+ const int rev = doc->availableUndoSteps();
if (rev == current)
showBlackMessage(FakeVimHandler::tr("Already at newest change"));
else
if (m_undoCursorPosition.contains(rev))
m_tc.setPosition(m_undoCursorPosition[rev]);
+ setTargetColumn();
+}
+
+void FakeVimHandler::Private::updateCursor()
+{
+ if (m_mode == ExMode || m_subsubmode == SearchSubSubMode) {
+ EDITOR(setCursorWidth(0));
+ EDITOR(setOverwriteMode(false));
+ } else if (m_mode == InsertMode) {
+ EDITOR(setCursorWidth(m_cursorWidth));
+ EDITOR(setOverwriteMode(false));
+ } else {
+ EDITOR(setCursorWidth(m_cursorWidth));
+ EDITOR(setOverwriteMode(true));
+ }
+}
+
+void FakeVimHandler::Private::enterReplaceMode()
+{
+ m_mode = ReplaceMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix.clear();
+ m_lastInsertion.clear();
+ m_lastDeletion.clear();
+ updateCursor();
}
void FakeVimHandler::Private::enterInsertMode()
{
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(false));
- //leaveVisualMode();
m_mode = InsertMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix.clear();
m_lastInsertion.clear();
- m_beginEditBlock = true;
+ m_lastDeletion.clear();
+ updateCursor();
}
void FakeVimHandler::Private::enterCommandMode()
{
- EDITOR(setCursorWidth(m_cursorWidth));
- EDITOR(setOverwriteMode(true));
if (atEndOfLine())
moveLeft();
m_mode = CommandMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix.clear();
+ updateCursor();
}
void FakeVimHandler::Private::enterExMode()
{
- EDITOR(setCursorWidth(0));
- EDITOR(setOverwriteMode(false));
m_mode = ExMode;
+ m_submode = NoSubMode;
+ m_subsubmode = NoSubSubMode;
+ m_commandPrefix = ":";
+ updateCursor();
}
void FakeVimHandler::Private::recordJump()
UNDO_DEBUG("jumps: " << m_jumpListUndo);
}
-void FakeVimHandler::Private::recordNewUndo()
-{
- //endEditBlock();
- UNDO_DEBUG("---- BREAK ----");
- //beginEditBlock();
-}
-
Column FakeVimHandler::Private::indentation(const QString &line) const
{
int ts = config(ConfigTabStop).toInt();
++pos;
text.truncate(pos);
// FIXME: handle 'smartindent' and 'cindent'
- m_tc.insertText(text);
+ insertText(text);
m_justAutoIndented = text.size();
}
}
return false;
m_tc.movePosition(StartOfLine, KeepAnchor);
m_tc.removeSelectedText();
+ fixMarks(m_tc.position(), -m_justAutoIndented);
m_lastInsertion.chop(m_justAutoIndented);
m_justAutoIndented = 0;
return true;
void FakeVimHandler::Private::replay(const QString &command, int n)
{
- //qDebug() << "REPLAY: " << command;
- m_inReplay = true;
+ //qDebug() << "REPLAY: " << quoteUnprintable(command);
+ g.inReplay = true;
for (int i = n; --i >= 0; ) {
foreach (QChar c, command) {
//qDebug() << " REPLAY: " << QString(c);
handleKey(Input(c));
}
}
- m_inReplay = false;
+ g.inReplay = false;
}
-
void FakeVimHandler::Private::selectWordTextObject(bool inner)
{
Q_UNUSED(inner); // FIXME
setAnchor();
// FIXME: Rework the 'anchor' concept.
if (isVisualMode())
- m_marks['<'] = m_tc.position();
+ setMark('<', m_tc.position());
moveToWordBoundary(false, true, true);
m_movetype = MoveInclusive;
}
setAnchor();
// FIXME: Rework the 'anchor' concept.
if (isVisualMode())
- m_marks['<'] = m_tc.position();
+ setMark('<', m_tc.position());
moveToWordBoundary(true, true, true);
m_movetype = MoveInclusive;
}
Q_UNUSED(type);
}
+int FakeVimHandler::Private::mark(int code) const
+{
+ // FIXME: distinguish local and global marks.
+ //qDebug() << "MARK: " << code << m_marks.value(code, -1) << m_marks;
+ return m_marks.value(code, -1);
+}
+
+void FakeVimHandler::Private::setMark(int code, int position)
+{
+ // FIXME: distinguish local and global marks.
+ m_marks[code] = position;
+}
///////////////////////////////////////////////////////////////////////
//
{
bool active = theFakeVimSetting(ConfigUseFakeVim)->value().toBool();
+ // Catch mouse events on the viewport.
+ QWidget *viewport = 0;
+ if (d->m_plaintextedit)
+ viewport = d->m_plaintextedit->viewport();
+ else if (d->m_textedit)
+ viewport = d->m_textedit->viewport();
+ if (ob == viewport) {
+ if (active && ev->type() == QEvent::MouseButtonRelease) {
+ QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
+ if (mev->button() == Qt::LeftButton) {
+ d->importSelection();
+ //return true;
+ }
+ }
+ if (active && ev->type() == QEvent::MouseButtonPress) {
+ QMouseEvent *mev = static_cast<QMouseEvent *>(ev);
+ if (mev->button() == Qt::LeftButton) {
+ d->m_visualMode = NoVisualMode;
+ d->updateSelection();
+ }
+ }
+ return QObject::eventFilter(ob, ev);
+ }
+
if (active && ev->type() == QEvent::Shortcut) {
d->passShortcuts(false);
return false;
d->m_currentFileName = fileName;
}
+QString FakeVimHandler::currentFileName() const
+{
+ return d->m_currentFileName;
+}
+
void FakeVimHandler::showBlackMessage(const QString &msg)
{
d->showBlackMessage(msg);
namespace FakeVim {
namespace Internal {
+enum RangeMode
+{
+ RangeCharMode, // v
+ RangeLineMode, // V
+ RangeLineModeExclusive,
+ RangeBlockMode, // Ctrl-v
+ RangeBlockAndTailMode, // Ctrl-v for D and X
+};
+
+struct Range
+{
+ Range();
+ Range(int b, int e, RangeMode m = RangeCharMode);
+ QString toString() const;
+
+ int beginPos;
+ int endPos;
+ RangeMode rangemode;
+};
+
+struct ExCommand
+{
+ ExCommand() : hasBang(false) {}
+ ExCommand(const QString &cmd, const QString &args = QString(),
+ const Range &range = Range());
+
+ QString cmd;
+ bool hasBang;
+ QString args;
+ Range range;
+};
+
class FakeVimHandler : public QObject
{
Q_OBJECT
public slots:
void setCurrentFileName(const QString &fileName);
+ QString currentFileName() const;
+
void showBlackMessage(const QString &msg);
void showRedMessage(const QString &msg);
void statusDataChanged(const QString &msg);
void extraInformationChanged(const QString &msg);
void selectionChanged(const QList<QTextEdit::ExtraSelection> &selection);
- void writeFileRequested(bool *handled,
- const QString &fileName, const QString &contents);
void writeAllRequested(QString *error);
void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
void checkForElectricCharacter(bool *result, QChar c);
void windowCommandRequested(int key);
void findRequested(bool reverse);
void findNextRequested(bool reverse);
- void handleExCommandRequested(const QString &cmd);
- void handleSetCommandRequested(bool *handled, const QString &cmd);
+ void handleExCommandRequested(bool *handled, const ExCommand &cmd);
public:
class Private;
} // namespace Internal
} // namespace FakeVim
+Q_DECLARE_METATYPE(FakeVim::Internal::ExCommand);
+
+
#endif // FAKEVIM_HANDLER_H
</property>
</widget>
</item>
+ <item row="2" column="2">
+ <widget class="QCheckBox" name="checkBoxShowMarks">
+ <property name="text">
+ <string>Show position of text marks</string>
+ </property>
+ </widget>
+ </item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="checkBoxSmartTab">
<property name="text">
#include <indenter.h>
#include <QtCore/QDebug>
+#include <QtCore/QFile>
#include <QtCore/QtPlugin>
#include <QtCore/QObject>
-#include <QtCore/QPoint>
#include <QtCore/QSettings>
#include <QtCore/QTextStream>
namespace FakeVim {
namespace Internal {
+typedef QMap<QString, QRegExp> CommandMap;
+
class FakeVimOptionPage : public Core::IOptionsPage
{
Q_OBJECT
m_ui.checkBoxHlSearch);
m_group.insert(theFakeVimSetting(ConfigShiftWidth),
m_ui.spinBoxShiftWidth);
+ m_group.insert(theFakeVimSetting(ConfigShowMarks),
+ m_ui.checkBoxShowMarks);
m_group.insert(theFakeVimSetting(ConfigSmartTab),
m_ui.checkBoxSmartTab);
QTextStream(&m_searchKeywords)
<< ' ' << m_ui.checkBoxAutoIndent->text()
<< ' ' << m_ui.checkBoxExpandTab->text()
+ << ' ' << m_ui.checkBoxShowMarks->text()
<< ' ' << m_ui.checkBoxSmartIndent->text()
- << ' ' << m_ui.checkBoxExpandTab->text()
<< ' ' << m_ui.checkBoxHlSearch->text()
<< ' ' << m_ui.checkBoxIncSearch->text()
<< ' ' << m_ui.checkBoxSmartTab->text()
namespace FakeVim {
namespace Internal {
-static QMap<QString, QRegExp> s_exCommandMap;
-static QMap<QString, QRegExp> s_defaultExCommandMap;
-
-
class FakeVimExCommandsPage : public Core::CommandMappings
{
Q_OBJECT
public:
- FakeVimExCommandsPage() {}
+ FakeVimExCommandsPage(FakeVimPluginPrivate *q) : m_q(q) {}
// IOptionsPage
QString id() const { return QLatin1String(Constants::SETTINGS_EX_CMDS_ID); }
QWidget *createPage(QWidget *parent);
void initialize();
+ CommandMap &exCommandMap();
+ CommandMap &defaultExCommandMap();
public slots:
void commandChanged(QTreeWidgetItem *current);
private:
void setRegex(const QString ®ex);
QList<CommandItem *> m_citems;
+ FakeVimPluginPrivate *m_q;
};
QWidget *FakeVimExCommandsPage::createPage(QWidget *parent)
QTreeWidgetItem *item = new QTreeWidgetItem;
ci->m_cmd = c;
ci->m_item = item;
- m_citems << ci;
+ m_citems.append(ci);
const QString name = uidm->stringForUniqueIdentifier(c->id());
const int pos = name.indexOf(QLatin1Char('.'));
const QString section = name.left(pos);
- const QString subId = name.mid(pos+1);
+ const QString subId = name.mid(pos + 1);
if (!sections.contains(section)) {
- QTreeWidgetItem *categoryItem = new QTreeWidgetItem(commandList(), QStringList() << section);
+ QTreeWidgetItem *categoryItem =
+ new QTreeWidgetItem(commandList(), QStringList() << section);
QFont f = categoryItem->font(0);
f.setBold(true);
categoryItem->setFont(0, f);
item->setText(0, subId);
if (c->action()) {
- QString text = c->hasAttribute(Command::CA_UpdateText) && !c->defaultText().isNull() ? c->defaultText() : c->action()->text();
+ QString text = c->hasAttribute(Command::CA_UpdateText)
+ && !c->defaultText().isNull()
+ ? c->defaultText() : c->action()->text();
text.remove(QRegExp("&(?!&)"));
item->setText(1, text);
} else {
item->setText(1, c->shortcut()->whatsThis());
}
- if (s_exCommandMap.contains(name)) {
- ci->m_regex = s_exCommandMap[name].pattern();
+ if (exCommandMap().contains(name)) {
+ ci->m_regex = exCommandMap()[name].pattern();
} else {
ci->m_regex.clear();
}
item->setText(2, ci->m_regex);
item->setData(0, Qt::UserRole, qVariantFromValue(ci));
- if (ci->m_regex != s_defaultExCommandMap[name].pattern())
+ if (ci->m_regex != defaultExCommandMap()[name].pattern())
setModified(item, true);
}
if (current->data(0, Qt::UserRole).isValid()) {
citem->m_regex = targetEdit()->text();
current->setText(2, citem->m_regex);
- s_exCommandMap[name] = QRegExp(citem->m_regex);
+ exCommandMap()[name] = QRegExp(citem->m_regex);
}
- if (citem->m_regex != s_defaultExCommandMap[name].pattern())
+ if (citem->m_regex != defaultExCommandMap()[name].pattern())
setModified(current, true);
else
setModified(current, false);
if (current && current->data(0, Qt::UserRole).isValid()) {
CommandItem *citem = qVariantValue<CommandItem *>(current->data(0, Qt::UserRole));
const QString &name = uidm->stringForUniqueIdentifier(citem->m_cmd->id());
- if (s_defaultExCommandMap.contains(name))
- setRegex(s_defaultExCommandMap[name].pattern());
+ if (defaultExCommandMap().contains(name))
+ setRegex(defaultExCommandMap()[name].pattern());
else
setRegex(QString());
}
UniqueIDManager *uidm = UniqueIDManager::instance();
foreach (CommandItem *item, m_citems) {
const QString &name = uidm->stringForUniqueIdentifier(item->m_cmd->id());
- if (s_defaultExCommandMap.contains(name)) {
- item->m_regex = s_defaultExCommandMap[name].pattern();
+ if (defaultExCommandMap().contains(name)) {
+ item->m_regex = defaultExCommandMap()[name].pattern();
} else {
item->m_regex.clear();
}
FakeVimPluginPrivate(FakeVimPlugin *);
~FakeVimPluginPrivate();
friend class FakeVimPlugin;
+ friend class FakeVimExCommandsPage;
bool initialize();
void aboutToShutdown();
void showCommandBuffer(const QString &contents);
void showExtraInformation(const QString &msg);
void changeSelection(const QList<QTextEdit::ExtraSelection> &selections);
- void writeFile(bool *handled, const QString &fileName, const QString &contents);
void moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor);
void checkForElectricCharacter(bool *result, QChar c);
void indentRegion(int *amount, int beginLine, int endLine, QChar typedChar);
- void handleExCommand(const QString &cmd);
- void handleSetCommand(bool *handled, QString cmd);
+ void handleExCommand(bool *handled, const ExCommand &cmd);
void handleDelayedQuitAll(bool forced);
void handleDelayedQuit(bool forced, Core::IEditor *editor);
FakeVimExCommandsPage *m_fakeVimExCommandsPage;
QHash<Core::IEditor *, FakeVimHandler *> m_editorToHandler;
- void triggerAction(const QString& code);
- void setActionChecked(const QString& code, bool check);
+ void triggerAction(const QString &code);
+ void setActionChecked(const QString &code, bool check);
void readSettings(QSettings *settings);
void writeSettings(QSettings *settings);
+
+ CommandMap &exCommandMap() { return m_exCommandMap; }
+ CommandMap &defaultExCommandMap() { return m_exCommandMap; }
+ CommandMap m_exCommandMap;
+ CommandMap m_defaultExCommandMap;
};
} // namespace Internal
q = plugin;
m_fakeVimOptionsPage = 0;
m_fakeVimExCommandsPage = 0;
-
- s_defaultExCommandMap[Constants::CMD_FILE_NEXT] =
+ defaultExCommandMap()[Constants::CMD_FILE_NEXT] =
QRegExp("^n(ext)?!?( (.*))?$");
- s_defaultExCommandMap[Constants::CMD_FILE_PREV] =
+ defaultExCommandMap()[Constants::CMD_FILE_PREV] =
QRegExp("^(N(ext)?|prev(ious)?)!?( (.*))?$");
- s_defaultExCommandMap[CppTools::Constants::SWITCH_HEADER_SOURCE] =
+ defaultExCommandMap()[CppTools::Constants::SWITCH_HEADER_SOURCE] =
QRegExp("^A$");
- s_defaultExCommandMap[ProjectExplorer::Constants::BUILD] =
- QRegExp("^make$");
- s_defaultExCommandMap["Coreplugin.OutputPane.previtem"] =
+ defaultExCommandMap()["Coreplugin.OutputPane.previtem"] =
QRegExp("^(cN(ext)?|cp(revious)?)!?( (.*))?$");
- s_defaultExCommandMap["Coreplugin.OutputPane.nextitem"] =
+ defaultExCommandMap()["Coreplugin.OutputPane.nextitem"] =
QRegExp("^cn(ext)?!?( (.*))?$");
- s_defaultExCommandMap[CppEditor::Constants::JUMP_TO_DEFINITION] =
+ defaultExCommandMap()[CppEditor::Constants::JUMP_TO_DEFINITION] =
QRegExp("^tag?$");
- s_defaultExCommandMap[Core::Constants::GO_BACK] =
+ defaultExCommandMap()[Core::Constants::GO_BACK] =
QRegExp("^pop?$");
+ defaultExCommandMap()[QLatin1String("QtCreator.Locate")] =
+ QRegExp("^e$");
}
FakeVimPluginPrivate::~FakeVimPluginPrivate()
q->addObject(m_fakeVimOptionsPage);
theFakeVimSettings()->readSettings(Core::ICore::instance()->settings());
- m_fakeVimExCommandsPage = new FakeVimExCommandsPage;
+ m_fakeVimExCommandsPage = new FakeVimExCommandsPage(this);
q->addObject(m_fakeVimExCommandsPage);
readSettings(Core::ICore::instance()->settings());
connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
this, SLOT(editorOpened(Core::IEditor*)));
- connect(theFakeVimSetting(SettingsDialog), SIGNAL(triggered()),
- this, SLOT(showSettingsDialog()));
connect(theFakeVimSetting(ConfigUseFakeVim), SIGNAL(valueChanged(QVariant)),
this, SLOT(setUseFakeVim(QVariant)));
connect(theFakeVimSetting(ConfigReadVimRc), SIGNAL(valueChanged(QVariant)),
cmd->setAttribute(Command::CA_Hide);
connect(switchFilePrevAction, SIGNAL(triggered()), this, SLOT(switchFilePrev()));
- // Delayed operatiosn
+ // Delayed operations.
connect(this, SIGNAL(delayedQuitRequested(bool,Core::IEditor*)),
this, SLOT(handleDelayedQuit(bool,Core::IEditor*)), Qt::QueuedConnection);
connect(this, SIGNAL(delayedQuitAllRequested(bool)),
settings->beginWriteArray(QLatin1String(exCommandMapGroup));
int count = 0;
- typedef QMap<QString, QRegExp>::const_iterator Iterator;
- const Iterator end = s_exCommandMap.constEnd();
- for (Iterator it = s_exCommandMap.constBegin(); it != end; ++it) {
+ typedef CommandMap::const_iterator Iterator;
+ const Iterator end = exCommandMap().constEnd();
+ for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
const QString &id = it.key();
const QRegExp &re = it.value();
- if ((s_defaultExCommandMap.contains(id) && s_defaultExCommandMap[id] != re)
- || (!s_defaultExCommandMap.contains(id) && !re.pattern().isEmpty())) {
+ if ((defaultExCommandMap().contains(id) && defaultExCommandMap()[id] != re)
+ || (!defaultExCommandMap().contains(id) && !re.pattern().isEmpty())) {
settings->setArrayIndex(count);
settings->setValue(QLatin1String(idKey), id);
settings->setValue(QLatin1String(reKey), re.pattern());
void FakeVimPluginPrivate::readSettings(QSettings *settings)
{
- s_exCommandMap = s_defaultExCommandMap;
+ exCommandMap() = defaultExCommandMap();
int size = settings->beginReadArray(QLatin1String(exCommandMapGroup));
for (int i = 0; i < size; ++i) {
settings->setArrayIndex(i);
const QString id = settings->value(QLatin1String(idKey)).toString();
const QString re = settings->value(QLatin1String(reKey)).toString();
- s_exCommandMap[id] = QRegExp(re);
+ exCommandMap()[id] = QRegExp(re);
}
settings->endArray();
}
QLatin1String(Constants::SETTINGS_ID));
}
-void FakeVimPluginPrivate::triggerAction(const QString& code)
+void FakeVimPluginPrivate::triggerAction(const QString &code)
{
Core::ActionManager *am = Core::ICore::instance()->actionManager();
QTC_ASSERT(am, return);
Core::Command *cmd = am->command(code);
- QTC_ASSERT(cmd, return);
+ QTC_ASSERT(cmd, qDebug() << "UNKNOW CODE: " << code; return);
QAction *action = cmd->action();
QTC_ASSERT(action, return);
action->trigger();
}
-void FakeVimPluginPrivate::setActionChecked(const QString& code, bool check)
+void FakeVimPluginPrivate::setActionChecked(const QString &code, bool check)
{
Core::ActionManager *am = Core::ICore::instance()->actionManager();
QTC_ASSERT(am, return);
this, SLOT(showExtraInformation(QString)));
connect(handler, SIGNAL(commandBufferChanged(QString)),
this, SLOT(showCommandBuffer(QString)));
- connect(handler, SIGNAL(writeFileRequested(bool*,QString,QString)),
- this, SLOT(writeFile(bool*,QString,QString)));
connect(handler, SIGNAL(selectionChanged(QList<QTextEdit::ExtraSelection>)),
this, SLOT(changeSelection(QList<QTextEdit::ExtraSelection>)));
connect(handler, SIGNAL(moveToMatchingParenthesis(bool*,bool*,QTextCursor*)),
connect(handler, SIGNAL(findNextRequested(bool)),
this, SLOT(findNext(bool)));
- connect(handler, SIGNAL(handleExCommandRequested(QString)),
- this, SLOT(handleExCommand(QString)));
- connect(handler, SIGNAL(handleSetCommandRequested(bool *,QString)),
- this, SLOT(handleSetCommand(bool *,QString)));
+ connect(handler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
+ this, SLOT(handleExCommand(bool*,ExCommand)));
handler->setCurrentFileName(editor->file()->fileName());
handler->installEventFilter();
*result = bt->isElectricCharacter(c);
}
-void FakeVimPluginPrivate::writeFile(bool *handled,
- const QString &fileName, const QString &contents)
+void FakeVimPluginPrivate::handleExCommand(bool *handled, const ExCommand &cmd)
{
- Q_UNUSED(contents)
-
- FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
- if (!handler)
- return;
-
- Core::IEditor *editor = m_editorToHandler.key(handler);
- if (editor && editor->file()->fileName() == fileName) {
- // Handle that as a special case for nicer interaction with core
- Core::IFile *file = editor->file();
- Core::ICore::instance()->fileManager()->blockFileChange(file);
- file->save(fileName);
- Core::ICore::instance()->fileManager()->unblockFileChange(file);
- *handled = true;
- }
-}
-
-void FakeVimPluginPrivate::handleExCommand(const QString &cmd)
-{
- static QRegExp reWriteAll("^wa(ll)?!?$");
- static QRegExp reQuit("^q!?$");
- static QRegExp reQuitAll("^qa!?$");
-
using namespace Core;
+ //qDebug() << "PLUGIN HANDLE: " << cmd.cmd;
+
+ *handled = false;
FakeVimHandler *handler = qobject_cast<FakeVimHandler *>(sender());
if (!handler)
EditorManager *editorManager = EditorManager::instance();
QTC_ASSERT(editorManager, return);
- if (reWriteAll.indexIn(cmd) != -1) {
+ *handled = true;
+ if (cmd.cmd == "w" || cmd.cmd == "write") {
+ Core::IEditor *editor = m_editorToHandler.key(handler);
+ const QString fileName = handler->currentFileName();
+ if (editor && editor->file()->fileName() == fileName) {
+ // Handle that as a special case for nicer interaction with core
+ Core::IFile *file = editor->file();
+ Core::ICore::instance()->fileManager()->blockFileChange(file);
+ file->save(fileName);
+ Core::ICore::instance()->fileManager()->unblockFileChange(file);
+ // Check result by reading back.
+ QFile file3(fileName);
+ file3.open(QIODevice::ReadOnly);
+ QByteArray ba = file3.readAll();
+ handler->showBlackMessage(FakeVimHandler::tr("\"%1\" %2 %3L, %4C written")
+ .arg(fileName).arg(" ")
+ .arg(ba.count('\n')).arg(ba.size()));
+ } else {
+ handler->showRedMessage(tr("File not saved"));
+ }
+ } else if (cmd.cmd == "wa" || cmd.cmd == "wall") {
// :wa
FileManager *fm = ICore::instance()->fileManager();
QList<IFile *> toSave = fm->modifiedFiles();
handler->showBlackMessage(tr("Saving succeeded"));
else
handler->showRedMessage(tr("%n files not saved", 0, failed.size()));
- } else if (reQuit.indexIn(cmd) != -1) {
- // :q
- bool forced = cmd.contains(QChar('!'));
- emit delayedQuitRequested(forced, m_editorToHandler.key(handler));
- } else if (reQuitAll.indexIn(cmd) != -1) {
+ } else if (cmd.cmd == "q" || cmd.cmd == "quit") {
+ // :q[uit]
+ emit delayedQuitRequested(cmd.hasBang, m_editorToHandler.key(handler));
+ } else if (cmd.cmd == "qa" || cmd.cmd == "qall") {
// :qa
- bool forced = cmd.contains(QChar('!'));
- emit delayedQuitAllRequested(forced);
+ emit delayedQuitAllRequested(cmd.hasBang);
+ } else if (cmd.cmd == "sp" || cmd.cmd == "split") {
+ // :sp[lit]
+ triggerAction(Core::Constants::SPLIT);
+ } else if (cmd.cmd == "vs" || cmd.cmd == "vsplit") {
+ // :vs[plit]
+ triggerAction(Core::Constants::SPLIT_SIDE_BY_SIDE);
+ } else if (cmd.cmd == "mak" || cmd.cmd == "make") {
+ // :mak[e][!] [arguments]
+ triggerAction(ProjectExplorer::Constants::BUILD);
+ } else if (cmd.cmd == "se" || cmd.cmd == "set") {
+ if (cmd.args.isEmpty()) {
+ // :set
+ showSettingsDialog();
+ } else if (cmd.args == "ic" || cmd.args == "ignorecase") {
+ // :set noic
+ setActionChecked(Find::Constants::CASE_SENSITIVE, false);
+ *handled = false; // Let the handler see it as well.
+ } else if (cmd.args == "noic" || cmd.args == "noignorecase") {
+ // :set noic
+ setActionChecked(Find::Constants::CASE_SENSITIVE, true);
+ *handled = false; // Let the handler see it as well.
+ }
} else {
- typedef QMap<QString, QRegExp>::const_iterator Iterator;
- const Iterator end = s_exCommandMap.constEnd();
- for (Iterator it = s_exCommandMap.constBegin(); it != end; ++it) {
+ // Check whether one of the configure commands matches.
+ typedef CommandMap::const_iterator Iterator;
+ const Iterator end = exCommandMap().constEnd();
+ for (Iterator it = exCommandMap().constBegin(); it != end; ++it) {
const QString &id = it.key();
const QRegExp &re = it.value();
-
- if (!re.pattern().isEmpty() && re.indexIn(cmd) != -1) {
+ if (!re.pattern().isEmpty() && re.indexIn(cmd.cmd) != -1) {
triggerAction(id);
return;
}
}
-
- handler->showRedMessage(tr("Not an editor command: %1").arg(cmd));
- }
-}
-
-void FakeVimPluginPrivate::handleSetCommand(bool *handled, QString cmd)
-{
- *handled = false;
- bool value = true;
- if (cmd.startsWith("no")) {
- value = false;
- cmd = cmd.mid(2);
- }
-
- if (cmd == "ic" || cmd == "ignorecase") {
- setActionChecked(Find::Constants::CASE_SENSITIVE, value);
- *handled = true;
+ *handled = false;
}
}
switchFile(true);
}
+CommandMap &FakeVimExCommandsPage::exCommandMap()
+{
+ return m_q->exCommandMap();
+}
+
+CommandMap &FakeVimExCommandsPage::defaultExCommandMap()
+{
+ return m_q->defaultExCommandMap();
+}
+
+
///////////////////////////////////////////////////////////////////////
//
// FakeVimPlugin
-<plugin name="Find" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Find" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Provides the find widget and the hooks for find implementations.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
}
else {
clearFindSupport();
- m_currentFind = 0;
}
}
}
void CurrentDocumentFind::candidateAggregationChanged()
{
- if (m_candidateWidget && m_candidateWidget!=m_currentWidget) {
+ if (m_candidateWidget && m_candidateWidget != m_currentWidget) {
m_candidateFind = Aggregation::query<IFindSupport>(m_candidateWidget);
emit candidateChanged();
}
-<plugin name="GenericProjectManager" version="1.3.84" compatVersion="1.3.84">
+<plugin name="GenericProjectManager" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Generic support</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="CppTools" version="1.3.84"/>
- <dependency name="CppEditor" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="CppTools" version="2.0.80"/>
+ <dependency name="CppEditor" version="2.0.80"/>
</dependencyList>
</plugin>
return false;
}
+void GenericProjectFile::rename(const QString &newName)
+{
+ // Can't happen
+ Q_UNUSED(newName);
+ Q_ASSERT(false);
+}
+
Core::IFile::ReloadBehavior GenericProjectFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
{
Q_UNUSED(state)
virtual bool isModified() const;
virtual bool isReadOnly() const;
virtual bool isSaveAsAllowed() const;
+ virtual void rename(const QString &newName);
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
void reload(ReloadFlag flag, ChangeType type);
return true;
}
-QList<ProjectExplorer::ProjectNode::ProjectAction> GenericProjectNode::supportedActions() const
+QList<ProjectExplorer::ProjectNode::ProjectAction> GenericProjectNode::supportedActions(Node *node) const
{
+ Q_UNUSED(node);
return QList<ProjectAction>()
<< AddFile
<< RemoveFile;
virtual bool hasBuildTargets() const;
- virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions() const;
+ virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions(Node *node) const;
virtual bool addSubProjects(const QStringList &proFilePaths);
virtual bool removeSubProjects(const QStringList &proFilePaths);
-<plugin name="ScmGit" version="1.3.84" compatVersion="1.3.84">
+<plugin name="ScmGit" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Git integration.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="VCSBase" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="VCSBase" version="2.0.80"/>
</dependencyList>
</plugin>
return rc;
}
+bool GitClient::synchronousMove(const QString &workingDirectory,
+ const QString &from,
+ const QString &to)
+{
+ if (Git::Constants::debug)
+ qDebug() << Q_FUNC_INFO << workingDirectory << from << to;
+ QByteArray outputText;
+ QByteArray errorText;
+ QStringList arguments;
+ arguments << QLatin1String("mv");
+ arguments << (from);
+ arguments << (to);
+ const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
+ if (!rc) {
+ const QString errorMessage = tr("Unable to move from %1 to %2: %3").
+ arg(from, to, commandOutputFromLocal8Bit(errorText));
+ outputWindow()->appendError(errorMessage);
+ }
+ return rc;
+}
+
bool GitClient::synchronousReset(const QString &workingDirectory,
const QStringList &files,
QString *errorMessage)
bool synchronousDelete(const QString &workingDirectory,
bool force,
const QStringList &files);
+ bool synchronousMove(const QString &workingDirectory,
+ const QString &from,
+ const QString &to);
bool synchronousReset(const QString &workingDirectory,
const QStringList &files = QStringList(),
QString *errorMessage = 0);
case DeleteOperation:
rc = true;
break;
+ case MoveOperation:
+ rc = true;
+ break;
case OpenOperation:
break;
case CreateRepositoryOperation:
return gitClient()->synchronousDelete(fi.absolutePath(), true, QStringList(fi.fileName()));
}
+bool GitVersionControl::vcsMove(const QString &from, const QString &to)
+{
+ const QFileInfo fromInfo(from);
+ const QFileInfo toInfo(to);
+ return gitClient()->synchronousMove(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
+}
+
bool GitVersionControl::vcsCreateRepository(const QString &directory)
{
return gitClient()->synchronousInit(directory);
&& gitClient()->synchronousStashRemove(topLevel, stashName);
}
-bool GitVersionControl::managesDirectory(const QString &directory) const
-{
- return !GitClient::findRepositoryForDirectory(directory).isEmpty();
-}
-
-QString GitVersionControl::findTopLevelForDirectory(const QString &directory) const
+bool GitVersionControl::managesDirectory(const QString &directory, QString *topLevel) const
{
- return GitClient::findRepositoryForDirectory(directory);
+ const QString topLevelFound = GitClient::findRepositoryForDirectory(directory);
+ if (topLevel)
+ *topLevel = topLevelFound;
+ return !topLevelFound.isEmpty();
}
bool GitVersionControl::vcsAnnotate(const QString &file, int line)
virtual QString displayName() const;
- bool managesDirectory(const QString &directory) const;
- virtual QString findTopLevelForDirectory(const QString &directory) const;
+ virtual bool managesDirectory(const QString &directory, QString *topLevel) const;
virtual bool supportsOperation(Operation operation) const;
virtual bool vcsOpen(const QString &fileName);
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
+ virtual bool vcsMove(const QString &from, const QString &to);
virtual bool vcsCreateRepository(const QString &directory);
virtual QString vcsCreateSnapshot(const QString &topLevel);
virtual QStringList vcsSnapshots(const QString &topLevel);
-<plugin name="HelloWorld" version="1.3.84" compatVersion="1.3.84">
+<plugin name="HelloWorld" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Hello World sample plugin.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
-<plugin name="Help" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Help" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Help system.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="Find" version="1.3.84"/>
- <dependency name="Locator" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="Find" version="2.0.80"/>
+ <dependency name="Locator" version="2.0.80"/>
</dependencyList>
</plugin>
-<plugin name="Locator" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Locator" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Provides the Locator widget and the hooks for Locator filter implementations.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
-<plugin name="Mercurial" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Mercurial" version="2.0.80" compatVersion="2.0.80">
<vendor>Brian McGillion</vendor>
<copyright>(C) 2008-2009 Brian McGillion</copyright>
<license>
<description>Mercurial integration.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="VCSBase" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="VCSBase" version="2.0.80"/>
</dependencyList>
</plugin>
return executeHgSynchronously(workingDir, args, &stdOut);
}
+bool MercurialClient::move(const QString &workingDir, const QString &from, const QString &to)
+{
+ QStringList args;
+ args << QLatin1String("rename") << from << to;
+ QByteArray stdOut;
+ return executeHgSynchronously(workingDir, args, &stdOut);
+}
+
bool MercurialClient::manifestSync(const QString &repository, const QString &relativeFilename)
{
// This only works when called from the repo and outputs paths relative to it.
~MercurialClient();
bool add(const QString &workingDir, const QString &fileName);
bool remove(const QString &workingDir, const QString &fileName);
+ bool move(const QString &workingDir, const QString &from, const QString &to);
bool manifestSync(const QString &repository, const QString &filename);
QString branchQuerySync(const QString &repositoryRoot);
bool parentRevisionsSync(const QString &workingDirectory,
return tr("Mercurial");
}
-bool MercurialControl::managesDirectory(const QString &directory) const
+bool MercurialControl::managesDirectory(const QString &directory, QString *topLevel) const
{
QFileInfo dir(directory);
- return !mercurialClient->findTopLevelForFile(dir).isEmpty();
-}
-
-QString MercurialControl::findTopLevelForDirectory(const QString &directory) const
-{
- QFileInfo dir(directory);
- return mercurialClient->findTopLevelForFile(dir);
+ const QString topLevelFound = mercurialClient->findTopLevelForFile(dir);
+ if (topLevel)
+ *topLevel = topLevelFound;
+ return !topLevelFound.isEmpty();
}
bool MercurialControl::supportsOperation(Operation operation) const
switch (operation) {
case Core::IVersionControl::AddOperation:
case Core::IVersionControl::DeleteOperation:
+ case Core::IVersionControl::MoveOperation:
case Core::IVersionControl::CreateRepositoryOperation:
case Core::IVersionControl::AnnotateOperation:
break;
return mercurialClient->remove(fi.absolutePath(), fi.fileName());
}
+bool MercurialControl::vcsMove(const QString &from, const QString &to)
+{
+ const QFileInfo fromInfo(from);
+ const QFileInfo toInfo(to);
+ return mercurialClient->move(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
+}
+
bool MercurialControl::vcsCreateRepository(const QString &directory)
{
return mercurialClient->createRepositorySync(directory);
bool MercurialControl::sccManaged(const QString &filename)
{
const QFileInfo fi(filename);
- const QString topLevel = findTopLevelForDirectory(fi.absolutePath());
- if (topLevel.isEmpty())
+ QString topLevel;
+ const bool managed = managesDirectory(fi.absolutePath(), &topLevel);
+ if (!managed || topLevel.isEmpty())
return false;
const QDir topLevelDir(topLevel);
return mercurialClient->manifestSync(topLevel, topLevelDir.relativeFilePath(filename));
explicit MercurialControl(MercurialClient *mercurialClient);
QString displayName() const;
- bool managesDirectory(const QString &filename) const;
- QString findTopLevelForDirectory(const QString &directory) const;
+ bool managesDirectory(const QString &filename, QString *topLevel = 0) const;
bool supportsOperation(Operation operation) const;
bool vcsOpen(const QString &fileName);
bool vcsAdd(const QString &filename);
bool vcsDelete(const QString &filename);
+ bool vcsMove(const QString &from, const QString &to);
bool vcsCreateRepository(const QString &directory);
QString vcsCreateSnapshot(const QString &topLevel);
QStringList vcsSnapshots(const QString &topLevel);
-<plugin name="Perforce" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Perforce" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Perforce integration.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="VCSBase" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="VCSBase" version="2.0.80"/>
</dependencyList>
</plugin>
m_updateAllAction->setEnabled(true);
}
-bool PerforcePlugin::managesDirectory(const QString &directory)
+bool PerforcePlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */)
+{
+ const bool rc = managesDirectoryFstat(directory);
+ if (topLevel) {
+ if (rc) {
+ *topLevel = m_settings.topLevelSymLinkTarget();
+ } else {
+ topLevel->clear();
+ }
+ }
+ return rc;
+}
+
+bool PerforcePlugin::managesDirectoryFstat(const QString &directory)
{
if (!m_settings.isValid())
return false;
return managed;
}
-QString PerforcePlugin::findTopLevelForDirectory(const QString &dir)
-{
- if (!m_settings.isValid())
- return QString();
- return managesDirectory(dir) ? m_settings.topLevelSymLinkTarget() : QString();
-}
-
bool PerforcePlugin::vcsOpen(const QString &workingDir, const QString &fileName)
{
if (Perforce::Constants::debug)
return !deleteResult.error;
}
+bool PerforcePlugin::vcsMove(const QString &workingDir, const QString &from, const QString &to)
+{
+ // TODO verify this works
+ QStringList args;
+ args << QLatin1String("edit") << from;
+ const PerforceResponse editResult = runP4Cmd(workingDir, args,
+ CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
+ if (editResult.error)
+ return false;
+ args.clear();
+ args << QLatin1String("move") << from << to;
+ const PerforceResponse moveResult = runP4Cmd(workingDir, args,
+ CommandToWindow|StdOutToWindow|StdErrToWindow|ErrorToWindow);
+ return !moveResult.error;
+}
+
static QString formatCommand(const QString &cmd, const QStringList &args)
{
const QChar blank = QLatin1Char(' ');
bool initialize(const QStringList &arguments, QString *error_message);
void extensionsInitialized();
- bool managesDirectory(const QString &directory);
- QString findTopLevelForDirectory(const QString &directory);
+ bool managesDirectory(const QString &directory, QString *topLevel = 0);
bool vcsOpen(const QString &workingDir, const QString &fileName);
bool vcsAdd(const QString &workingDir, const QString &fileName);
bool vcsDelete(const QString &workingDir, const QString &filename);
+ bool vcsMove(const QString &workingDir, const QString &from, const QString &to);
void p4Diff(const QString &workingDir, const QStringList &files);
void updateCheckout(const QString &workingDir = QString(),
const QStringList &dirs = QStringList());
bool revertProject(const QString &workingDir, const QStringList &args, bool unchangedOnly);
+ bool managesDirectoryFstat(const QString &directory);
inline PerforceVersionControl *perforceVersionControl() const;
switch (operation) {
case AddOperation:
case DeleteOperation:
+ case MoveOperation:
case OpenOperation:
case AnnotateOperation:
return true;
return m_plugin->vcsDelete(fi.absolutePath(), fi.fileName());
}
+bool PerforceVersionControl::vcsMove(const QString &from, const QString &to)
+{
+ const QFileInfo fromInfo(from);
+ const QFileInfo toInfo(to);
+ return m_plugin->vcsMove(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
+}
+
bool PerforceVersionControl::vcsCreateRepository(const QString &)
{
return false;
return true;
}
-bool PerforceVersionControl::managesDirectory(const QString &directory) const
-{
- const bool rc = m_plugin->managesDirectory(directory);
- if (Perforce::Constants::debug)
- qDebug() << "managesDirectory" << directory << rc;
- return rc;
-}
-
-QString PerforceVersionControl::findTopLevelForDirectory(const QString &directory) const
+bool PerforceVersionControl::managesDirectory(const QString &directory, QString *topLevel) const
{
- const QString rc = m_plugin->findTopLevelForDirectory(directory);
- if (Perforce::Constants::debug)
- qDebug() << "findTopLevelForDirectory" << directory << rc;
+ const bool rc = m_plugin->managesDirectory(directory, topLevel);
+ if (Perforce::Constants::debug) {
+ QDebug nsp = qDebug().nospace();
+ nsp << "managesDirectory" << directory << rc;
+ if (topLevel)
+ nsp << topLevel;
+ }
return rc;
}
virtual QString displayName() const;
- bool managesDirectory(const QString &directory) const;
- virtual QString findTopLevelForDirectory(const QString &directory) const;
+ virtual bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
+
virtual bool supportsOperation(Operation operation) const;
virtual bool vcsOpen(const QString &fileName);
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
+ virtual bool vcsMove(const QString &from, const QString &to);
virtual bool vcsCreateRepository(const QString &directory);
virtual QString vcsCreateSnapshot(const QString &topLevel);
virtual QStringList vcsSnapshots(const QString &topLevel);
-<plugin name="ProjectExplorer" version="1.3.84" compatVersion="1.3.84">
+<plugin name="ProjectExplorer" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>ProjectExplorer framework that can be extended with different kind of project types.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="Find" version="1.3.84"/>
- <dependency name="Locator" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="Find" version="2.0.80"/>
+ <dependency name="Locator" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
</dependencyList>
<argumentList>
<argument name="-customwizard-verbose">Verbose loading of custom wizards</argument>
void ApplicationLauncher::stop()
{
+ if (!isRunning())
+ return;
if (m_currentMode == Gui) {
m_winGuiProcess->stop();
} else {
m_consoleProcess->stop();
+ processStopped();
}
}
void ApplicationLauncher::stop()
{
+ if (!isRunning())
+ return;
if (m_currentMode == Gui) {
m_guiProcess->terminate();
if (!m_guiProcess->waitForFinished(1000)) { // This is blocking, so be fast.
}
} else {
m_consoleProcess->stop();
+ processStopped();
}
}
RunControl *LocalApplicationRunControlFactory::create(ProjectExplorer::RunConfiguration *runConfiguration, const QString &mode)
{
QTC_ASSERT(canRun(runConfiguration, mode), return 0);
- return new LocalApplicationRunControl(qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration));
+ return new LocalApplicationRunControl(qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration), mode);
}
QWidget *LocalApplicationRunControlFactory::createConfigurationWidget(RunConfiguration *runConfiguration)
// ApplicationRunControl
-LocalApplicationRunControl::LocalApplicationRunControl(LocalApplicationRunConfiguration *runConfiguration)
- : RunControl(runConfiguration)
+LocalApplicationRunControl::LocalApplicationRunControl(LocalApplicationRunConfiguration *runConfiguration, QString mode)
+ : RunControl(runConfiguration, mode)
{
m_applicationLauncher.setEnvironment(runConfiguration->environment().toStringList());
m_applicationLauncher.setWorkingDirectory(runConfiguration->workingDirectory());
{
Q_OBJECT
public:
- LocalApplicationRunControl(LocalApplicationRunConfiguration *runConfiguration);
+ LocalApplicationRunControl(LocalApplicationRunConfiguration *runConfiguration, QString mode);
virtual ~LocalApplicationRunControl();
virtual void start();
virtual void stop();
--- /dev/null
+/**************************************************************************
+**
+** 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 "buildconfigurationmodel.h"
+#include "target.h"
+#include "buildconfiguration.h"
+
+using namespace ProjectExplorer;
+
+///
+/// RunConfigurationsModel
+///
+
+class BuildConfigurationComparer
+{
+public:
+ bool operator()(BuildConfiguration *a, BuildConfiguration *b)
+ {
+ return a->displayName() < b->displayName();
+ }
+};
+
+BuildConfigurationModel::BuildConfigurationModel(Target *target, QObject *parent)
+ : QAbstractListModel(parent),
+ m_target(target)
+{
+ m_buildConfigurations = m_target->buildConfigurations();
+ qSort(m_buildConfigurations.begin(), m_buildConfigurations.end(), BuildConfigurationComparer());
+
+ connect(target, SIGNAL(addedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
+ this, SLOT(addedBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
+ connect(target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
+ this, SLOT(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
+
+ foreach (BuildConfiguration *bc, m_buildConfigurations)
+ connect(bc, SIGNAL(displayNameChanged()),
+ this, SLOT(displayNameChanged()));
+}
+
+int BuildConfigurationModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : m_buildConfigurations.size();
+}
+
+int BuildConfigurationModel::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : 1;
+}
+
+void BuildConfigurationModel::displayNameChanged()
+{
+ BuildConfiguration *rc = qobject_cast<BuildConfiguration *>(sender());
+ if (!rc)
+ return;
+
+ BuildConfigurationComparer compare;
+ // Find the old position
+ int oldPos = m_buildConfigurations.indexOf(rc);
+
+ if (oldPos >= 1 && compare(m_buildConfigurations.at(oldPos), m_buildConfigurations.at(oldPos - 1))) {
+ // We need to move up
+ int newPos = oldPos - 1;
+ while (newPos >= 0 && compare(m_buildConfigurations.at(oldPos), m_buildConfigurations.at(newPos))) {
+ --newPos;
+ }
+ ++newPos;
+
+ beginMoveRows(QModelIndex(), oldPos, oldPos, QModelIndex(), newPos);
+ m_buildConfigurations.insert(newPos, rc);
+ m_buildConfigurations.removeAt(oldPos + 1);
+ endMoveRows();
+ // Not only did we move, we also changed...
+ emit dataChanged(index(newPos, 0), index(newPos,0));
+ } else if (oldPos < m_buildConfigurations.size() - 1
+ && compare(m_buildConfigurations.at(oldPos + 1), m_buildConfigurations.at(oldPos))) {
+ // We need to move down
+ int newPos = oldPos + 1;
+ while (newPos < m_buildConfigurations.size()
+ && compare(m_buildConfigurations.at(newPos), m_buildConfigurations.at(oldPos))) {
+ ++newPos;
+ }
+ beginMoveRows(QModelIndex(), oldPos, oldPos, QModelIndex(), newPos);
+ m_buildConfigurations.insert(newPos, rc);
+ m_buildConfigurations.removeAt(oldPos);
+ endMoveRows();
+
+ // We need to subtract one since removing at the old place moves the newIndex down
+ emit dataChanged(index(newPos - 1, 0), index(newPos - 1, 0));
+ } else {
+ emit dataChanged(index(oldPos, 0), index(oldPos, 0));
+ }
+}
+
+QVariant BuildConfigurationModel::data(const QModelIndex &index, int role) const
+{
+ if (role == Qt::DisplayRole) {
+ const int row = index.row();
+ if (row < m_buildConfigurations.size()) {
+ return m_buildConfigurations.at(row)->displayName();
+ }
+ }
+
+ return QVariant();
+}
+
+BuildConfiguration *BuildConfigurationModel::buildConfigurationAt(int i)
+{
+ if (i > m_buildConfigurations.size() || i < 0)
+ return 0;
+ return m_buildConfigurations.at(i);
+}
+
+BuildConfiguration *BuildConfigurationModel::buildConfigurationFor(const QModelIndex &idx)
+{
+ if (idx.row() > m_buildConfigurations.size() || idx.row() < 0)
+ return 0;
+ return m_buildConfigurations.at(idx.row());
+}
+
+QModelIndex BuildConfigurationModel::indexFor(BuildConfiguration *rc)
+{
+ int idx = m_buildConfigurations.indexOf(rc);
+ if (idx == -1)
+ return QModelIndex();
+ return index(idx, 0);
+}
+
+void BuildConfigurationModel::addedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc)
+{
+ // Find the right place to insert
+ BuildConfigurationComparer compare;
+ int i = 0;
+ for (; i < m_buildConfigurations.size(); ++i) {
+ if (compare(bc, m_buildConfigurations.at(i))) {
+ break;
+ }
+ }
+
+ beginInsertRows(QModelIndex(), i, i);
+ m_buildConfigurations.insert(i, bc);
+ endInsertRows();
+
+
+ connect(bc, SIGNAL(displayNameChanged()),
+ this, SLOT(displayNameChanged()));
+}
+
+void BuildConfigurationModel::removedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc)
+{
+ int i = m_buildConfigurations.indexOf(bc);
+ beginRemoveRows(QModelIndex(), i, i);
+ m_buildConfigurations.removeAt(i);
+ endRemoveRows();
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 BUILDCONFIGURATIONMODEL_H
+#define BUILDCONFIGURATIONMODEL_H
+
+#include <QtCore/QAbstractItemModel>
+
+namespace ProjectExplorer {
+class Target;
+class BuildConfiguration;
+
+/*! A model to represent the build configurations of a target.
+ To be used in for the drop down of comboboxes
+ Does automatically adjust itself to added and removed BuildConfigurations
+ Very similar to the Run Configuration Model
+ TOOD might it possible to share code without making the code a complete mess
+*/
+class BuildConfigurationModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ BuildConfigurationModel(Target *target, QObject *parent = 0);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ BuildConfiguration *buildConfigurationAt(int i);
+ BuildConfiguration *buildConfigurationFor(const QModelIndex &idx);
+ QModelIndex indexFor(BuildConfiguration *rc);
+private slots:
+ void addedBuildConfiguration(ProjectExplorer::BuildConfiguration*);
+ void removedBuildConfiguration(ProjectExplorer::BuildConfiguration*);
+ void displayNameChanged();
+private:
+ Target *m_target;
+ QList<BuildConfiguration *> m_buildConfigurations;
+};
+
+}
+
+#endif // RUNCONFIGURATIONMODEL_H
#include "project.h"
#include "target.h"
#include "buildconfiguration.h"
+#include "buildconfigurationmodel.h"
#include <coreplugin/coreconstants.h>
#include <extensionsystem/pluginmanager.h>
hbox->addWidget(new QLabel(tr("Edit build configuration:"), this));
m_buildConfigurationComboBox = new QComboBox(this);
m_buildConfigurationComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ m_buildConfigurationComboBox->setModel(new BuildConfigurationModel(m_target, this));
hbox->addWidget(m_buildConfigurationComboBox);
m_addButton = new QPushButton(this);
}
m_buildConfiguration = m_target->activeBuildConfiguration();
+ BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildConfigurationComboBox->model());
+ m_buildConfigurationComboBox->setCurrentIndex(model->indexFor(m_buildConfiguration).row());
updateAddButtonMenu();
updateBuildSettings();
connect(m_target, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
this, SLOT(updateActiveConfiguration()));
- connect(m_target, SIGNAL(addedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
- this, SLOT(addedBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
-
- connect(m_target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
- this, SLOT(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
-
- foreach (BuildConfiguration *bc, m_target->buildConfigurations()) {
- connect(bc, SIGNAL(displayNameChanged()),
- this, SLOT(buildConfigurationDisplayNameChanged()));
- }
-
if (m_target->buildConfigurationFactory())
connect(m_target->buildConfigurationFactory(), SIGNAL(availableCreationIdsChanged()),
SLOT(updateAddButtonMenu()));
}
-void BuildSettingsWidget::addedBuildConfiguration(BuildConfiguration *bc)
-{
- connect(bc, SIGNAL(displayNameChanged()),
- this, SLOT(buildConfigurationDisplayNameChanged()));
-}
-
-void BuildSettingsWidget::removedBuildConfiguration(BuildConfiguration *bc)
-{
- disconnect(bc, SIGNAL(displayNameChanged()),
- this, SLOT(buildConfigurationDisplayNameChanged()));
-}
-
-void BuildSettingsWidget::buildConfigurationDisplayNameChanged()
-{
- for (int i = 0; i < m_buildConfigurationComboBox->count(); ++i) {
- BuildConfiguration *bc = m_buildConfigurationComboBox->itemData(i).value<BuildConfiguration *>();
- m_buildConfigurationComboBox->setItemText(i, bc->displayName());
- }
-}
-
void BuildSettingsWidget::addSubWidget(const QString &name, BuildConfigWidget *widget)
{
widget->setContentsMargins(m_leftMargin, 10, 0, 0);
void BuildSettingsWidget::updateBuildSettings()
{
- // Delete old tree items
- bool blocked = m_buildConfigurationComboBox->blockSignals(true);
- m_buildConfigurationComboBox->clear();
clear();
// update buttons
foreach (BuildConfigWidget *subConfigWidget, subConfigWidgets)
addSubWidget(subConfigWidget->displayName(), subConfigWidget);
- // Add tree items
- foreach (BuildConfiguration *bc, m_target->buildConfigurations()) {
- m_buildConfigurationComboBox->addItem(bc->displayName(), QVariant::fromValue<BuildConfiguration *>(bc));
- if (bc == m_buildConfiguration)
- m_buildConfigurationComboBox->setCurrentIndex(m_buildConfigurationComboBox->count() - 1);
- }
-
foreach (BuildConfigWidget *widget, subWidgets())
widget->init(m_buildConfiguration);
-
- m_buildConfigurationComboBox->blockSignals(blocked);
}
void BuildSettingsWidget::currentIndexChanged(int index)
{
- BuildConfiguration *buildConfiguration = m_buildConfigurationComboBox->itemData(index).value<BuildConfiguration *>();
+ BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildConfigurationComboBox->model());
+ BuildConfiguration *buildConfiguration = model->buildConfigurationAt(index);
m_target->setActiveBuildConfiguration(buildConfiguration);
}
m_buildConfiguration = m_target->activeBuildConfiguration();
- for (int i = 0; i < m_buildConfigurationComboBox->count(); ++i) {
- if (m_buildConfigurationComboBox->itemData(i).value<BuildConfiguration *>() == m_buildConfiguration) {
- m_buildConfigurationComboBox->setCurrentIndex(i);
- break;
- }
- }
+ BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildConfigurationComboBox->model());
+ m_buildConfigurationComboBox->setCurrentIndex(model->indexFor(m_buildConfiguration).row());
foreach (QWidget *widget, subWidgets()) {
if (BuildConfigWidget *buildStepWidget = qobject_cast<BuildConfigWidget*>(widget)) {
void deleteConfiguration();
void updateAddButtonMenu();
- void addedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc);
- void removedBuildConfiguration(ProjectExplorer::BuildConfiguration *bc);
- void buildConfigurationDisplayNameChanged();
void updateActiveConfiguration();
private:
#include <QtGui/QLabel>
#include <QtGui/QRegExpValidator>
#include <QtGui/QComboBox>
+#include <QtGui/QTextEdit>
enum { debug = 0 };
{
}
+CustomWizardFieldPage::TextEditData::TextEditData(QTextEdit* le, const QString &defText) :
+ textEdit(le), defaultText(defText)
+{
+}
+
CustomWizardFieldPage::CustomWizardFieldPage(const QSharedPointer<CustomWizardContext> &ctx,
const FieldList &fields,
QWidget *parent) :
m_context(ctx),
m_formLayout(new QFormLayout)
{
+ m_formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
if (debug)
qDebug() << Q_FUNC_INFO << fields.size();
foreach(const CustomWizardField &f, fields)
QWidget *fieldWidget = 0;
if (className == QLatin1String("QComboBox")) {
fieldWidget = registerComboBox(fieldName, field);
+ } else if (className == QLatin1String("QTextEdit")) {
+ fieldWidget = registerTextEdit(fieldName, field);
+ } else if (className == QLatin1String("Utils::PathChooser")) {
+ fieldWidget = registerPathChooser(fieldName, field);
} else {
fieldWidget = registerLineEdit(fieldName, field);
}
return combo;
} // QComboBox
+QWidget *CustomWizardFieldPage::registerTextEdit(const QString &fieldName,
+ const CustomWizardField &field)
+{
+ QTextEdit *textEdit = new QTextEdit;
+ registerField(fieldName, textEdit, "plainText", SIGNAL(textChanged(QString)));
+ const QString defaultText = field.controlAttributes.value(QLatin1String("defaulttext"));
+ m_textEdits.push_back(TextEditData(textEdit, defaultText));
+ return textEdit;
+} // QTextEdit
+
+QWidget *CustomWizardFieldPage::registerPathChooser(const QString &fieldName,
+ const CustomWizardField &field)
+{
+ Utils::PathChooser *pathChooser = new Utils::PathChooser;
+ registerField(fieldName, pathChooser, "path", SIGNAL(changed(QString)));
+ return pathChooser;
+} // Utils::PathChooser
+
QWidget *CustomWizardFieldPage::registerLineEdit(const QString &fieldName,
const CustomWizardField &field)
{
led.lineEdit->setText(defaultText);
}
}
+ foreach(const TextEditData &ted, m_textEdits) {
+ if (!ted.defaultText.isEmpty()) {
+ QString defaultText = ted.defaultText;
+ CustomWizardContext::replaceFields(m_context->baseReplacements, &defaultText);
+ ted.textEdit->setText(defaultText);
+ }
+ }
}
bool CustomWizardFieldPage::validatePage()
QT_BEGIN_NAMESPACE
class QFormLayout;
class QLineEdit;
+class QTextEdit;
QT_END_NAMESPACE
namespace Utils {
QLineEdit* lineEdit;
QString defaultText;
};
+ struct TextEditData {
+ explicit TextEditData(QTextEdit* le = 0, const QString &defText = QString());
+ QTextEdit* textEdit;
+ QString defaultText;
+ };
typedef QList<LineEditData> LineEditDataList;
+ typedef QList<TextEditData> TextEditDataList;
QWidget *registerLineEdit(const QString &fieldName, const CustomWizardField &field);
QWidget *registerComboBox(const QString &fieldName, const CustomWizardField &field);
-
+ QWidget *registerTextEdit(const QString &fieldName, const CustomWizardField &field);
+ QWidget *registerPathChooser(const QString &fieldName, const CustomWizardField &field);
void addField(const CustomWizardField &f);
const QSharedPointer<CustomWizardContext> m_context;
QFormLayout *m_formLayout;
LineEditDataList m_lineEdits;
+ TextEditDataList m_textEdits;
};
// A custom wizard page presenting the fields to be used and a path chooser
#endif
const QChar slash = QLatin1Char('/');
foreach (const QString &p, path()) {
-// qDebug()<<"trying"<<path + '/' + executable;
+// qDebug()<<"trying"<<p + '/' + executable;
QString fp = p;
fp += slash;
fp += executable;
#else
const QChar sep = QLatin1Char(':');
#endif
- return m_values.value(QLatin1String("PATH")).split(sep);
+ return m_values.value(QLatin1String("PATH")).split(sep, QString::SkipEmptyParts);
}
QString Environment::value(const QString &key) const
**************************************************************************/
#include "miniprojecttargetselector.h"
+#include "buildconfigurationmodel.h"
+#include "runconfigurationmodel.h"
#include <utils/qtcassert.h>
#include <utils/styledbar.h>
m_buildComboBox->setProperty("hideborder", true);
m_buildComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
m_buildComboBox->setToolTip(tr("Select active build configuration"));
+ m_buildComboBox->setModel(new BuildConfigurationModel(m_target, this));
} else {
m_buildComboBox = 0;
}
m_runComboBox ->setProperty("hideborder", true);
m_runComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
m_runComboBox->setToolTip(tr("Select active run configuration"));
+ RunConfigurationModel *model = new RunConfigurationModel(m_target, this);
+ m_runComboBox->setModel(model);
int fontSize = font().pointSize();
setStyleSheet(QString::fromLatin1("QLabel { font-size: %2pt; color: white; } "
"#target { font: bold %1pt;} "
Q_FOREACH(BuildConfiguration* bc, m_target->buildConfigurations())
addBuildConfiguration(bc);
+ BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
+ m_buildComboBox->setCurrentIndex(model->indexFor(m_target->activeBuildConfiguration()).row());
+
connect(m_target, SIGNAL(addedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
SLOT(addBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
connect(m_target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
connect(m_buildComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setActiveBuildConfiguration(int)));
}
- Q_FOREACH(RunConfiguration* rc, m_target->runConfigurations())
- addRunConfiguration(rc);
+ m_runComboBox->setEnabled(m_target->runConfigurations().count() > 1);
+ m_runComboBox->setCurrentIndex(model->indexFor(m_target->activeRunConfiguration()).row());
connect(m_target, SIGNAL(addedRunConfiguration(ProjectExplorer::RunConfiguration*)),
SLOT(addRunConfiguration(ProjectExplorer::RunConfiguration*)));
void MiniTargetWidget::setActiveBuildConfiguration(int index)
{
- ProjectExplorer::BuildConfiguration* bc =
- m_buildComboBox->itemData(index).value<ProjectExplorer::BuildConfiguration*>();
- m_target->setActiveBuildConfiguration(bc);
+ BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
+ m_target->setActiveBuildConfiguration(model->buildConfigurationAt(index));
emit changed();
}
void MiniTargetWidget::setActiveRunConfiguration(int index)
{
- m_target->setActiveRunConfiguration(
- m_runComboBox->itemData(index).value<ProjectExplorer::RunConfiguration*>());
+ RunConfigurationModel *model = static_cast<RunConfigurationModel *>(m_runComboBox->model());
+ m_target->setActiveRunConfiguration(model->runConfigurationAt(index));
updateIcon();
emit changed();
}
+
void MiniTargetWidget::setActiveBuildConfiguration()
{
- QTC_ASSERT(m_buildComboBox, return)
- m_buildComboBox->setCurrentIndex(m_buildComboBox->findData(
- QVariant::fromValue(m_target->activeBuildConfiguration())));
+ QTC_ASSERT(m_buildComboBox, return);
+ BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
+ m_buildComboBox->setCurrentIndex(model->indexFor(m_target->activeBuildConfiguration()).row());
}
void MiniTargetWidget::setActiveRunConfiguration()
{
- m_runComboBox->setCurrentIndex(m_runComboBox->findData(
- QVariant::fromValue(m_target->activeRunConfiguration())));
+ RunConfigurationModel *model = static_cast<RunConfigurationModel *>(m_runComboBox->model());
+ m_runComboBox->setCurrentIndex(model->indexFor(m_target->activeRunConfiguration()).row());
}
void MiniTargetWidget::addRunConfiguration(ProjectExplorer::RunConfiguration* rc)
{
- connect(rc, SIGNAL(displayNameChanged()), SLOT(updateDisplayName()));
- m_runComboBox->addItem(rc->displayName(), QVariant::fromValue(rc));
- if (m_target->activeRunConfiguration() == rc)
- m_runComboBox->setCurrentIndex(m_runComboBox->count()-1);
-
- m_runComboBox->setEnabled(m_runComboBox->count()>1);
+ Q_UNUSED(rc);
+ m_runComboBox->setEnabled(m_target->runConfigurations().count()>1);
}
void MiniTargetWidget::removeRunConfiguration(ProjectExplorer::RunConfiguration* rc)
{
- m_runComboBox->removeItem(m_runComboBox->findData(QVariant::fromValue(rc)));
- m_runComboBox->setEnabled(m_runComboBox->count()>1);
+ Q_UNUSED(rc);
+ m_runComboBox->setEnabled(m_target->runConfigurations().count()>1);
}
void MiniTargetWidget::addBuildConfiguration(ProjectExplorer::BuildConfiguration* bc)
{
+ Q_UNUSED(bc);
QTC_ASSERT(m_buildComboBox, return);
- connect(bc, SIGNAL(displayNameChanged()), SLOT(updateDisplayName()));
- m_buildComboBox->addItem(bc->displayName(), QVariant::fromValue(bc));
- if (m_target->activeBuildConfiguration() == bc)
- m_buildComboBox->setCurrentIndex(m_buildComboBox->count()-1);
-
- m_buildComboBox->setEnabled(m_buildComboBox->count() > 1);
+ connect(bc, SIGNAL(displayNameChanged()), SIGNAL(changed()), Qt::UniqueConnection);
+ m_buildComboBox->setEnabled(m_target->buildConfigurations().count() > 1);
}
void MiniTargetWidget::removeBuildConfiguration(ProjectExplorer::BuildConfiguration* bc)
{
+ Q_UNUSED(bc);
QTC_ASSERT(m_buildComboBox, return);
- m_buildComboBox->removeItem(m_buildComboBox->findData(QVariant::fromValue(bc)));
- m_buildComboBox->setEnabled(m_buildComboBox->count() > 1);
-}
-
-void MiniTargetWidget::updateDisplayName()
-{
- QObject *obj = sender();
- RunConfiguration *rc = qobject_cast<RunConfiguration*>(obj);
- BuildConfiguration *bc = qobject_cast<BuildConfiguration*>(obj);
- if (rc) {
- m_runComboBox->setItemText(m_runComboBox->findData(QVariant::fromValue(rc)),
- rc->displayName());
- } else if (bc) {
- m_buildComboBox->setItemText(m_buildComboBox->findData(QVariant::fromValue(bc)),
- bc->displayName());
- }
- emit changed();
+ m_buildComboBox->setEnabled(m_target->buildConfigurations().count() > 1);
}
bool MiniTargetWidget::hasBuildConfiguration() const
private slots:
void addRunConfiguration(ProjectExplorer::RunConfiguration *runConfig);
- void removeRunConfiguration(ProjectExplorer::RunConfiguration *buildConfig);
+ void removeRunConfiguration(ProjectExplorer::RunConfiguration *runConfig);
void addBuildConfiguration(ProjectExplorer::BuildConfiguration *buildConfig);
void removeBuildConfiguration(ProjectExplorer::BuildConfiguration *buildConfig);
void setActiveBuildConfiguration();
void setActiveRunConfiguration();
- void updateDisplayName();
void updateIcon();
signals:
#include "projectexplorer.h"
#include "projectexplorersettings.h"
#include "runconfiguration.h"
+#include "session.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/actioncontainer.h>
OutputPane::OutputPane()
: m_mainWidget(new QWidget)
{
- QIcon runIcon(Constants::ICON_RUN);
- runIcon.addFile(Constants::ICON_RUN_SMALL);
+ m_runIcon.addFile(Constants::ICON_RUN);
+ m_runIcon.addFile(Constants::ICON_RUN_SMALL);
+
+ m_debugIcon.addFile(Constants::ICON_DEBUG);
+ m_debugIcon.addFile(Constants::ICON_DEBUG_SMALL);
// Rerun
m_reRunButton = new QToolButton;
- m_reRunButton->setIcon(runIcon);
+ m_reRunButton->setIcon(m_runIcon);
m_reRunButton->setToolTip(tr("Re-run this run-configuration"));
m_reRunButton->setAutoRaise(true);
m_reRunButton->setEnabled(false);
connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
this, SLOT(coreAboutToClose()));
+
+ connect(ProjectExplorer::ProjectExplorerPlugin::instance()->session(), SIGNAL(aboutToUnloadSession()),
+ this, SLOT(aboutToUnloadSession()));
}
void OutputPane::coreAboutToClose()
}
}
+void OutputPane::aboutToUnloadSession()
+{
+ int i = 0;
+ while (i < m_tabWidget->count()) {
+ bool closed = closeTab(i);
+ if (!closed) // skip to next one
+ ++i;
+ }
+}
+
OutputPane::~OutputPane()
{
delete m_mainWidget;
rc->stop();
}
-void OutputPane::closeTab(int index)
+bool OutputPane::closeTab(int index)
{
OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(index));
RunControl *rc = m_outputWindows.key(ow);
if (rc->isRunning()) {
QMessageBox messageBox(QMessageBox::Warning,
tr("Unable to close"),
- tr("The application is still running."),
+ tr("%1 is still running.").arg(rc->displayName()),
QMessageBox::Cancel | QMessageBox::Yes,
ow->window());
messageBox.setInformativeText(tr("Force it to quit?"));
messageBox.setDefaultButton(QMessageBox::Yes);
messageBox.button(QMessageBox::Yes)->setText(tr("Force Quit"));
+ messageBox.button(QMessageBox::Cancel)->setText(tr("Keep Running"));
if (messageBox.exec() != QMessageBox::Yes)
- return;
+ return false;
rc->stop();
}
m_tabWidget->removeTab(index);
delete ow;
delete rc;
+ return true;
}
void OutputPane::projectRemoved()
RunControl *rc = runControlForTab(i);
m_stopAction->setEnabled(rc->isRunning());
m_reRunButton->setEnabled(!rc->isRunning());
+ m_reRunButton->setIcon( rc->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon);
}
}
if (rc == qobject_cast<RunControl *>(sender())) {
m_reRunButton->setEnabled(false);
m_stopAction->setEnabled(true);
+ m_reRunButton->setIcon( rc->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon);
}
}
if (rc == qobject_cast<RunControl *>(sender())) {
m_reRunButton->setEnabled(rc);
m_stopAction->setEnabled(false);
+ m_reRunButton->setIcon( rc->runMode() == Constants::DEBUGMODE ? m_debugIcon : m_runIcon);
}
}
#include <QtCore/QObject>
#include <QtCore/QHash>
+#include <QtGui/QIcon>
#include <QtGui/QShowEvent>
#include <QtGui/QPlainTextEdit>
private slots:
void reRunRunControl();
void stopRunControl();
- void closeTab(int index);
+ bool closeTab(int index);
void tabChanged(int);
void runControlStarted();
void runControlFinished();
+ void aboutToUnloadSession();
+
private:
RunControl *runControlForTab(int index) const;
QAction *m_stopAction;
QToolButton *m_reRunButton;
QToolButton *m_stopButton;
+ QIcon m_runIcon;
+ QIcon m_debugIcon;
};
SessionManager *session = pe->session();
if (session->projects().count() == 1)
fIFace = session->projects().first()->file();
- else if (session->projects().count() > 1)
- fIFace = session->file(); // TODO: Why return session file interface here ???
}
return fIFace;
}
globalcontext);
mfilec->addAction(cmd, Constants::G_FILE_OTHER);
- // renamefile action (TODO: Not supported yet)
+ // renamefile action
d->m_renameFileAction = new QAction(tr("Rename"), this);
cmd = am->registerAction(d->m_renameFileAction, ProjectExplorer::Constants::RENAMEFILE,
globalcontext);
mfilec->addAction(cmd, Constants::G_FILE_OTHER);
- d->m_renameFileAction->setEnabled(false);
- d->m_renameFileAction->setVisible(false);
+ // Not yet used by anyone, so hide for now
+// mfolder->addAction(cmd, Constants::G_FOLDER_FILES);
+// msubProject->addAction(cmd, Constants::G_FOLDER_FILES);
+// mproject->addAction(cmd, Constants::G_FOLDER_FILES);
// target selector
d->m_projectSelectorAction = new QAction(this);
QSettings *s = Core::ICore::instance()->settings();
if (s) {
- s->setValue("ProjectExplorer/StartupSession", d->m_session->file()->fileName());
+ s->setValue("ProjectExplorer/StartupSession", d->m_session->currentSession());
s->remove("ProjectExplorer/RecentProjects/Files");
QStringList fileNames;
contextMenu = d->m_sessionContextMenu;
}
- updateContextMenuActions();
+ updateContextMenuActions(d->m_currentNode);
if (contextMenu && contextMenu->actions().count() > 0) {
contextMenu->popup(globalPos);
}
d->m_buildManager->gotoTaskWindow();
}
-void ProjectExplorerPlugin::updateContextMenuActions()
+void ProjectExplorerPlugin::updateContextMenuActions(Node *node)
{
d->m_addExistingFilesAction->setEnabled(false);
d->m_addNewFileAction->setEnabled(false);
d->m_removeFileAction->setEnabled(false);
- if (FolderNode *folderNode = qobject_cast<FolderNode*>(d->m_currentNode)) {
- const bool addFilesEnabled = folderNode->projectNode()->supportedActions().contains(ProjectNode::AddFile);
+ QList<ProjectNode::ProjectAction> actions =
+ d->m_currentNode->projectNode()->supportedActions(node);
+
+ if (qobject_cast<FolderNode*>(d->m_currentNode)) {
+ bool addFilesEnabled = actions.contains(ProjectNode::AddFile);
d->m_addExistingFilesAction->setEnabled(addFilesEnabled);
d->m_addNewFileAction->setEnabled(addFilesEnabled);
- } else if (FileNode *fileNode = qobject_cast<FileNode*>(d->m_currentNode)) {
- const bool removeFileEnabled = fileNode->projectNode()->supportedActions().contains(ProjectNode::RemoveFile);
+ d->m_renameFileAction->setEnabled(actions.contains(ProjectNode::Rename));
+ } else if (qobject_cast<FileNode*>(d->m_currentNode)) {
+ bool removeFileEnabled = actions.contains(ProjectNode::RemoveFile);
d->m_removeFileAction->setEnabled(removeFileEnabled);
+ d->m_renameFileAction->setEnabled(actions.contains(ProjectNode::Rename));
}
}
FileNode *fileNode = qobject_cast<FileNode*>(d->m_currentNode);
Core::ICore *core = Core::ICore::instance();
- const QString filePath = d->m_currentNode->path();
- const QString fileDir = QFileInfo(filePath).dir().absolutePath();
+ QString filePath = d->m_currentNode->path();
RemoveFileDialog removeFileDialog(filePath, core->mainWindow());
if (removeFileDialog.exec() == QDialog::Accepted) {
}
}
+void ProjectExplorerPlugin::renameFile(Node *node, const QString &to)
+{
+ FileNode *fileNode = qobject_cast<FileNode *>(node);
+ if (!fileNode)
+ return;
+ QString orgFilePath = node->path();
+ QString dir = QFileInfo(orgFilePath).absolutePath();
+ QString newFilePath = dir + "/" + to;
+ Core::ICore *core = Core::ICore::instance();
+ Core::IVersionControl *vc = core->vcsManager()->findVersionControlForDirectory(dir);
+ bool result = false;
+ if (vc->supportsOperation(Core::IVersionControl::MoveOperation))
+ result = vc->vcsMove(orgFilePath, newFilePath);
+ if (!result) // The moving via vcs failed or the vcs does not support moving, fall back
+ result = QFile::rename(orgFilePath, newFilePath);
+ if (result) {
+ // yeah we moved, tell the filemanager about it
+ Core::ICore::instance()->fileManager()->renamedFile(orgFilePath, newFilePath);
+ // Tell the project plugin about it
+ ProjectNode *projectNode = fileNode->projectNode();
+ projectNode->renameFile(fileNode->fileType(), orgFilePath, newFilePath);
+ // TODO emit a signal?
+ }
+}
+
void ProjectExplorerPlugin::populateOpenWithMenu(QMenu *menu, const QString &fileName)
{
typedef QList<Core::IEditorFactory*> EditorFactoryList;
void startRunControl(RunControl *runControl, const QString &mode);
+ // internal public for FlatModel
+ void renameFile(Node *node, const QString &to);
+
signals:
void aboutToShowContextMenu(ProjectExplorer::Project *project,
ProjectExplorer::Node *node);
void savePersistentSettings();
void goToTaskWindow();
- void updateContextMenuActions();
+ void updateContextMenuActions(Node *node);
void addNewFile();
void addExistingFiles();
void openFile();
TEMPLATE = lib
TARGET = ProjectExplorer
QT += xml \
- script
+ script \
+ network
include(../../qtcreatorplugin.pri)
include(projectexplorer_dependencies.pri)
include(../../shared/scriptwrapper/scriptwrapper.pri)
buildsettingspropertiespage.h \
environmenteditmodel.h \
processstep.h \
- abstractprocessstep.h \
editorconfiguration.h \
editorsettingspropertiespage.h \
runconfiguration.h \
buildconfigdialog.h \
ldparser.h \
linuxiccparser.h \
- outputformatter.h
+ outputformatter.h \
+ runconfigurationmodel.h \
+ buildconfigurationmodel.h \
+ abstractprocessstep.h
SOURCES += projectexplorer.cpp \
projectwindow.cpp \
buildmanager.cpp \
buildconfigdialog.cpp \
ldparser.cpp \
linuxiccparser.cpp \
- outputformatter.cpp
+ outputformatter.cpp \
+ runconfigurationmodel.cpp \
+ buildconfigurationmodel.cpp
FORMS += processstep.ui \
editorsettingspropertiespage.ui \
runsettingspropertiespage.ui \
namespace Internal {
-// AllProjectNodesVisitor: Retrieve all projects (*.pri/*.pro).
+// AllProjectNodesVisitor: Retrieve all projects (*.pri/*.pro)
+// which support adding files
class AllProjectNodesVisitor : public NodesVisitor
{
public:
void AllProjectNodesVisitor::visitProjectNode(ProjectNode *node)
{
- if (node->supportedActions().contains(ProjectNode::AddFile))
+ if (node->supportedActions(node).contains(ProjectNode::AddFile))
m_projectNodes.push_back(node);
}
#include "project.h"
#include "projectexplorerconstants.h"
#include "projectnodes.h"
+#include "projectexplorer.h"
#include <coreplugin/fileiconprovider.h>
#include <utils/qtcassert.h>
const QString fileName2 = QFileInfo(file2->path()).fileName();
if (fileName1 != fileName2)
- return fileName1 < fileName2;
+ return fileName1.compare(fileName2, Qt::CaseInsensitive) < 0;
else
return file1 < file2;
} else {
ProjectNode *project2 = static_cast<ProjectNode*>(n2);
if (project1->displayName() != project2->displayName())
- return project1->displayName() < project2->displayName(); // sort by name
+ return project1->displayName().compare(project2->displayName(), Qt::CaseInsensitive) < 0; // sort by name
else
return project1 < project2; // sort by pointer value
} else {
FolderNode *folder2 = static_cast<FolderNode*>(n2);
if (folder1->displayName() != folder2->displayName())
- return folder1->displayName() < folder2->displayName();
+ return folder1->displayName().compare(folder2->displayName(), Qt::CaseInsensitive) < 0;
else
return folder1 < folder2;
} else {
const QString fileName2 = QFileInfo(filePath2).fileName();
if (fileName1 != fileName2) {
- return fileName1 < fileName2; // sort by file names
+ return fileName1.compare(fileName2, Qt::CaseInsensitive) < 0; // sort by file names
} else {
if (filePath1 != filePath2) {
- return filePath1 < filePath2; // sort by path names
+ return filePath1.compare(filePath2, Qt::CaseInsensitive) < 0; // sort by path names
} else {
return n1 < n2; // sort by pointer value
}
return result;
}
+Qt::ItemFlags FlatModel::flags(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return 0;
+ // We claim that everything is editable
+ // That's slightly wrong
+ // We control the only view, and that one does the checks
+ return Qt::ItemIsSelectable|Qt::ItemIsEnabled | Qt::ItemIsEditable;
+}
+
+bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ if (!index.isValid())
+ return false;
+ if (role != Qt::EditRole)
+ return false;
+
+ ProjectExplorerPlugin::instance()->renameFile(nodeForIndex(index), value.toString());
+ return true;
+}
+
int FlatModel::rowCount(const QModelIndex &parent) const
{
int rows = 0;
namespace Internal {
-class FlatModel : public QAbstractItemModel {
+class FlatModel : public QAbstractItemModel
+{
Q_OBJECT
public:
FlatModel(SessionNode *rootNode, QObject *parent);
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ Qt::ItemFlags flags(const QModelIndex &index) const;
+ bool setData(const QModelIndex &index, const QVariant &value, int role);
int rowCount(const QModelIndex & parent = QModelIndex()) const;
int columnCount(const QModelIndex & parent = QModelIndex()) const;
emit watcher->foldersAboutToBeAdded(this, folderNodes);
foreach (ProjectNode *project, subProjects) {
- QTC_ASSERT(!project->parentFolderNode(),
- qDebug("Project node has already a parent"));
+ QTC_ASSERT(!project->parentFolderNode() || project->parentFolderNode() == this,
+ qDebug("Project node has already a parent"));
project->setParentFolderNode(this);
foreach (NodesWatcher *watcher, m_watchers)
project->registerWatcher(watcher);
AddSubProject,
RemoveSubProject,
AddFile,
- RemoveFile
+ RemoveFile,
+ Rename
};
// all subFolders that are projects
// TODO find a better name
virtual bool hasBuildTargets() const = 0;
- virtual QList<ProjectAction> supportedActions() const = 0;
+ virtual QList<ProjectAction> supportedActions(Node *node) const = 0;
virtual bool addSubProjects(const QStringList &proFilePaths) = 0;
{
}
-RunControl::RunControl(RunConfiguration *runConfiguration)
- : m_runConfiguration(runConfiguration)
+RunControl::RunControl(RunConfiguration *runConfiguration, QString mode)
+ : m_runMode(mode), m_runConfiguration(runConfiguration)
{
if (runConfiguration)
m_displayName = runConfiguration->displayName();
}
+QString RunControl::runMode() const
+{
+ return m_runMode;
+}
+
QString RunControl::displayName() const
{
return m_displayName;
{
Q_OBJECT
public:
- explicit RunControl(RunConfiguration *runConfiguration);
+ explicit RunControl(RunConfiguration *runConfiguration, QString mode);
virtual ~RunControl();
virtual void start() = 0;
virtual void stop() = 0; // Warning: assumed to be synchroneous!
bool sameRunConfiguration(RunControl *other);
virtual OutputFormatter *createOutputFormatter(QObject *parent = 0);
+ QString runMode() const;
signals:
void addToOutputWindow(RunControl *, const QString &line, bool onStdErr);
private:
QString m_displayName;
+ QString m_runMode;
const QWeakPointer<RunConfiguration> m_runConfiguration;
#ifdef Q_OS_MAC
--- /dev/null
+/**************************************************************************
+**
+** 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 "runconfigurationmodel.h"
+#include "target.h"
+#include "runconfiguration.h"
+
+using namespace ProjectExplorer;
+
+///
+/// RunConfigurationsModel
+///
+
+class RunConfigurationComparer
+{
+public:
+ bool operator()(RunConfiguration *a, RunConfiguration *b)
+ {
+ return a->displayName() < b->displayName();
+ }
+};
+
+RunConfigurationModel::RunConfigurationModel(Target *target, QObject *parent)
+ : QAbstractListModel(parent),
+ m_target(target)
+{
+ m_runConfigurations = m_target->runConfigurations();
+ qSort(m_runConfigurations.begin(), m_runConfigurations.end(), RunConfigurationComparer());
+
+ connect(target, SIGNAL(addedRunConfiguration(ProjectExplorer::RunConfiguration*)),
+ this, SLOT(addedRunConfiguration(ProjectExplorer::RunConfiguration*)));
+ connect(target, SIGNAL(removedRunConfiguration(ProjectExplorer::RunConfiguration*)),
+ this, SLOT(removedRunConfiguration(ProjectExplorer::RunConfiguration*)));
+
+ foreach (RunConfiguration *rc, m_runConfigurations)
+ connect(rc, SIGNAL(displayNameChanged()),
+ this, SLOT(displayNameChanged()));
+}
+
+int RunConfigurationModel::rowCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : m_runConfigurations.size();
+}
+
+int RunConfigurationModel::columnCount(const QModelIndex &parent) const
+{
+ return parent.isValid() ? 0 : 1;
+}
+
+void RunConfigurationModel::displayNameChanged()
+{
+ RunConfiguration *rc = qobject_cast<RunConfiguration *>(sender());
+ if (!rc)
+ return;
+
+ RunConfigurationComparer compare;
+ // Find the old position
+ int oldPos = m_runConfigurations.indexOf(rc);
+
+ if (oldPos >= 1 && compare(m_runConfigurations.at(oldPos), m_runConfigurations.at(oldPos - 1))) {
+ // We need to move up
+ int newPos = oldPos - 1;
+ while (newPos >= 0 && compare(m_runConfigurations.at(oldPos), m_runConfigurations.at(newPos))) {
+ --newPos;
+ }
+ ++newPos;
+
+ beginMoveRows(QModelIndex(), oldPos, oldPos, QModelIndex(), newPos);
+ m_runConfigurations.insert(newPos, rc);
+ m_runConfigurations.removeAt(oldPos + 1);
+ endMoveRows();
+ // Not only did we move, we also changed...
+ emit dataChanged(index(newPos, 0), index(newPos,0));
+ } else if (oldPos < m_runConfigurations.size() - 1
+ && compare(m_runConfigurations.at(oldPos + 1), m_runConfigurations.at(oldPos))) {
+ // We need to move down
+ int newPos = oldPos + 1;
+ while (newPos < m_runConfigurations.size()
+ && compare(m_runConfigurations.at(newPos), m_runConfigurations.at(oldPos))) {
+ ++newPos;
+ }
+ beginMoveRows(QModelIndex(), oldPos, oldPos, QModelIndex(), newPos);
+ m_runConfigurations.insert(newPos, rc);
+ m_runConfigurations.removeAt(oldPos);
+ endMoveRows();
+
+ // We need to subtract one since removing at the old place moves the newIndex down
+ emit dataChanged(index(newPos - 1, 0), index(newPos - 1, 0));
+ } else {
+ emit dataChanged(index(oldPos, 0), index(oldPos, 0));
+ }
+}
+
+QVariant RunConfigurationModel::data(const QModelIndex &index, int role) const
+{
+ if (role == Qt::DisplayRole) {
+ const int row = index.row();
+ if (row < m_runConfigurations.size()) {
+ return m_runConfigurations.at(row)->displayName();
+ }
+ }
+
+ return QVariant();
+}
+
+RunConfiguration *RunConfigurationModel::runConfigurationAt(int i)
+{
+ if (i > m_runConfigurations.size() || i < 0)
+ return 0;
+ return m_runConfigurations.at(i);
+}
+
+RunConfiguration *RunConfigurationModel::runConfigurationFor(const QModelIndex &idx)
+{
+ if (idx.row() > m_runConfigurations.size() || idx.row() < 0)
+ return 0;
+ return m_runConfigurations.at(idx.row());
+}
+
+QModelIndex RunConfigurationModel::indexFor(RunConfiguration *rc)
+{
+ int idx = m_runConfigurations.indexOf(rc);
+ if (idx == -1)
+ return QModelIndex();
+ return index(idx, 0);
+}
+
+void RunConfigurationModel::addedRunConfiguration(ProjectExplorer::RunConfiguration *rc)
+{
+ // Find the right place to insert
+ RunConfigurationComparer compare;
+ int i = 0;
+ for (; i < m_runConfigurations.size(); ++i) {
+ if (compare(rc, m_runConfigurations.at(i))) {
+ break;
+ }
+ }
+
+ beginInsertRows(QModelIndex(), i, i);
+ m_runConfigurations.insert(i, rc);
+ endInsertRows();
+
+
+ connect(rc, SIGNAL(displayNameChanged()),
+ this, SLOT(displayNameChanged()));
+}
+
+void RunConfigurationModel::removedRunConfiguration(ProjectExplorer::RunConfiguration *rc)
+{
+ int i = m_runConfigurations.indexOf(rc);
+ beginRemoveRows(QModelIndex(), i, i);
+ m_runConfigurations.removeAt(i);
+ endRemoveRows();
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 RUNCONFIGURATIONMODEL_H
+#define RUNCONFIGURATIONMODEL_H
+
+#include <QtCore/QAbstractItemModel>
+
+namespace ProjectExplorer {
+class Target;
+class RunConfiguration;
+
+/*! A model to represent the run configurations of a target.
+ To be used in for the drop down of comboboxes
+ Does automatically adjust itself to added and removed RunConfigurations
+*/
+class RunConfigurationModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ RunConfigurationModel(Target *target, QObject *parent = 0);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+ RunConfiguration *runConfigurationAt(int i);
+ RunConfiguration *runConfigurationFor(const QModelIndex &idx);
+ QModelIndex indexFor(RunConfiguration *rc);
+private slots:
+ void addedRunConfiguration(ProjectExplorer::RunConfiguration*);
+ void removedRunConfiguration(ProjectExplorer::RunConfiguration*);
+ void displayNameChanged();
+private:
+ Target *m_target;
+ QList<RunConfiguration *> m_runConfigurations;
+};
+
+}
+
+#endif // RUNCONFIGURATIONMODEL_H
**************************************************************************/
#include "runsettingspropertiespage.h"
+#include "runconfigurationmodel.h"
#include "runconfiguration.h"
#include "target.h"
#include "project.h"
}
///
-/// RunConfigurationsModel
-///
-
-RunConfigurationsModel::RunConfigurationsModel(Target *target, QObject *parent)
- : QAbstractListModel(parent),
- m_target(target)
-{
- m_runConfigurations = m_target->runConfigurations();
- connect(target, SIGNAL(addedRunConfiguration(ProjectExplorer::RunConfiguration*)),
- this, SLOT(addedRunConfiguration(ProjectExplorer::RunConfiguration*)));
- connect(target, SIGNAL(removedRunConfiguration(ProjectExplorer::RunConfiguration*)),
- this, SLOT(removedRunConfiguration(ProjectExplorer::RunConfiguration*)));
-
- foreach (RunConfiguration *rc, m_runConfigurations)
- connect(rc, SIGNAL(displayNameChanged()),
- this, SLOT(displayNameChanged()));
-}
-
-int RunConfigurationsModel::rowCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : m_runConfigurations.size();
-}
-
-int RunConfigurationsModel::columnCount(const QModelIndex &parent) const
-{
- return parent.isValid() ? 0 : 1;
-}
-
-void RunConfigurationsModel::displayNameChanged()
-{
- RunConfiguration *rc = qobject_cast<RunConfiguration *>(sender());
- QTC_ASSERT(rc, return);
- for (int i = 0; i < m_runConfigurations.size(); ++i) {
- if (m_runConfigurations.at(i) == rc) {
- emit dataChanged(index(i, 0), index(i,0));
- break;
- }
- }
-}
-
-QVariant RunConfigurationsModel::data(const QModelIndex &index, int role) const
-{
- if (role == Qt::DisplayRole) {
- const int row = index.row();
- if (row < m_runConfigurations.size()) {
- return m_runConfigurations.at(row)->displayName();
- }
- }
-
- return QVariant();
-}
-
-RunConfiguration *RunConfigurationsModel::runConfigurationAt(int i)
-{
- if (i > m_runConfigurations.size() || i < 0)
- return 0;
- return m_runConfigurations.at(i);
-}
-
-RunConfiguration *RunConfigurationsModel::runConfigurationFor(const QModelIndex &idx)
-{
- if (idx.row() > m_runConfigurations.size() || idx.row() < 0)
- return 0;
- return m_runConfigurations.at(idx.row());
-}
-
-QModelIndex RunConfigurationsModel::indexFor(RunConfiguration *rc)
-{
- int idx = m_runConfigurations.indexOf(rc);
- if (idx == -1)
- return QModelIndex();
- return index(idx, 0);
-}
-
-void RunConfigurationsModel::addedRunConfiguration(ProjectExplorer::RunConfiguration *rc)
-{
- int i = m_target->runConfigurations().indexOf(rc);
- QTC_ASSERT(i > 0, return);
- beginInsertRows(QModelIndex(), i, i);
- m_runConfigurations.insert(i, rc);
- endInsertRows();
- QTC_ASSERT(m_runConfigurations == m_target->runConfigurations(), return);
- connect(rc, SIGNAL(displayNameChanged()),
- this, SLOT(displayNameChanged()));
-}
-
-void RunConfigurationsModel::removedRunConfiguration(ProjectExplorer::RunConfiguration *rc)
-{
- int i = m_runConfigurations.indexOf(rc);
- QTC_ASSERT(i >= 0, return);
- beginRemoveRows(QModelIndex(), i, i);
- m_runConfigurations.removeAt(i);
- endRemoveRows();
- QTC_ASSERT(m_runConfigurations == m_target->runConfigurations(), return);
-}
-
-
-///
/// RunSettingsWidget
///
RunSettingsWidget::RunSettingsWidget(Target *target)
: m_target(target),
- m_runConfigurationsModel(new RunConfigurationsModel(target, this)),
+ m_runConfigurationsModel(new RunConfigurationModel(target, this)),
m_runConfigurationWidget(0),
m_ignoreChange(false)
{
m_ui->removeToolButton->setText(tr("Remove"));
m_ui->runConfigurationCombo->setModel(m_runConfigurationsModel);
m_ui->runConfigurationCombo->setCurrentIndex(
- m_target->runConfigurations().indexOf(m_target->activeRunConfiguration()));
+ m_runConfigurationsModel->indexFor(m_target->activeRunConfiguration()).row());
m_ui->removeToolButton->setEnabled(m_target->runConfigurations().size() > 1);
namespace ProjectExplorer {
class RunConfiguration;
+class RunConfigurationModel;
namespace Internal {
class RunSettingsPropertiesPage;
}
-class RunConfigurationsModel;
class RunSettingsWidget;
class RunSettingsPanelFactory : public ITargetPanelFactory
void activeRunConfigurationChanged();
private:
Target *m_target;
- RunConfigurationsModel *m_runConfigurationsModel;
+ RunConfigurationModel *m_runConfigurationsModel;
Ui::RunSettingsPropertiesPage *m_ui;
QWidget *m_runConfigurationWidget;
QMenu *m_addMenu;
bool m_ignoreChange;
};
-/*! A model to represent the run configurations of a target. */
-class RunConfigurationsModel : public QAbstractListModel
-{
- Q_OBJECT
-public:
- RunConfigurationsModel(Target *target, QObject *parent = 0);
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
- int columnCount(const QModelIndex &parent = QModelIndex()) const;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
-
- RunConfiguration *runConfigurationAt(int i);
- RunConfiguration *runConfigurationFor(const QModelIndex &idx);
- QModelIndex indexFor(RunConfiguration *rc);
-private slots:
- void addedRunConfiguration(ProjectExplorer::RunConfiguration*);
- void removedRunConfiguration(ProjectExplorer::RunConfiguration*);
- void displayNameChanged();
-private:
- Target *m_target;
- QList<RunConfiguration *> m_runConfigurations;
-};
-
-
-
} // namespace Internal
} // namespace ProjectExplorer
namespace ProjectExplorer {
namespace Internal {
-class SessionFile : public Core::IFile
+class SessionFile : QObject
{
Q_OBJECT
QString fileName() const;
void setFileName(const QString &fileName);
- QString defaultPath() const;
- QString suggestedFileName() const;
- virtual QString mimeType() const;
-
- bool isModified() const;
- bool isReadOnly() const;
- bool isSaveAsAllowed() const;
-
- ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
- void reload(ReloadFlag flag, ChangeType type);
-
public slots:
void sessionLoadingProgress();
-
private:
- const QString m_mimeType;
Core::ICore *m_core;
QString m_fileName;
}
SessionFile::SessionFile()
- : m_mimeType(QLatin1String(ProjectExplorer::Constants::SESSIONFILE_MIMETYPE)),
- m_core(Core::ICore::instance()),
+ : m_core(Core::ICore::instance()),
m_startupProject(0)
{
}
-QString SessionFile::mimeType() const
-{
- return m_mimeType;
-}
-
bool SessionFile::load(const QString &fileName)
{
Q_ASSERT(!fileName.isEmpty());
m_fileName = fileName;
}
-bool SessionFile::isModified() const
-{
- return true;
-}
-
-bool SessionFile::isReadOnly() const
-{
- return false;
-}
-
-bool SessionFile::isSaveAsAllowed() const
-{
- return true;
-}
-
-Core::IFile::ReloadBehavior SessionFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
-{
- Q_UNUSED(state)
- Q_UNUSED(type)
- return BehaviorSilent;
-}
-
-void SessionFile::reload(ReloadFlag flag, ChangeType type)
-{
- Q_UNUSED(flag)
- Q_UNUSED(type)
-}
-
-QString SessionFile::defaultPath() const
-{
- if (!m_projects.isEmpty()) {
- const QFileInfo fi(m_projects.first()->file()->fileName());
- return fi.absolutePath();
- }
- return QString();
-}
-
-QString SessionFile::suggestedFileName() const
-{
- if (m_startupProject)
- return m_startupProject->displayName();
-
- return tr("Untitled", "default file name to display");
-}
-
Internal::SessionNodeImpl::SessionNodeImpl(SessionManager *manager)
- : ProjectExplorer::SessionNode(manager->file()->fileName(), manager)
+ : ProjectExplorer::SessionNode(manager->currentSession(), manager)
{
setFileName("session");
}
textEditor->setTextCodec(project->editorConfiguration()->defaultTextCodec());
}
-Core::IFile *SessionManager::file() const
+QString SessionManager::currentSession() const
{
- return m_file;
+ return m_file->fileName();
}
void SessionManager::handleCurrentEditorChange(Core::IEditor *editor)
bool addDependency(Project *project, Project *depProject);
void removeDependency(Project *project, Project *depProject);
- Core::IFile *file() const;
+ QString currentSession() const;
Project *startupProject() const;
const QList<Project *> &projects() const;
#include "target.h"
#include "toolchain.h"
+#include <coreplugin/icore.h>
+#include <coreplugin/mainwindow.h>
#include <coreplugin/ifile.h>
#include <utils/qtcassert.h>
-#include <QtCore/QCoreApplication>
+#include <QtGui/QApplication>
#include <QtCore/QFile>
+#include <QtGui/QMessageBox>
+#include <QtNetwork/QHostInfo>
using namespace ProjectExplorer;
namespace {
-const char * const USER_FILE_VERSION("ProjectExplorer.Project.Updater.FileVersion");
-const char * const WAS_UPDATED("ProjectExplorer.Project.Updater.DidUpdate");
+const char * const USER_FILE_VERSION = "ProjectExplorer.Project.Updater.FileVersion";
+const char * const USER_FILE_HOSTNAME = "ProjectExplorer.Project.Updater.Hostname";
+const char * const WAS_UPDATED = "ProjectExplorer.Project.Updater.DidUpdate";
const char * const PROJECT_FILE_POSTFIX(".user");
// Version 0 is used in Qt Creator 1.3.x and
if (m_lastVersion < 0 || !project)
return QVariantMap();
- QString fileName(fileNameFor(project->file()->fileName()));
+ QString fileName = fileNameFor(project->file()->fileName());
if (!QFile::exists(fileName))
return QVariantMap();
QVariantMap map(reader.restoreValues());
// Get and verify file version:
- const int fileVersion(map.value(QLatin1String(USER_FILE_VERSION), 0).toInt());
+ const int fileVersion = map.value(QLatin1String(USER_FILE_VERSION), 0).toInt();
if (fileVersion < m_firstVersion || fileVersion > m_lastVersion + 1) {
qWarning() << "File version" << fileVersion << "is not supported.";
return QVariantMap();
}
+ // Verify hostname
+ const QString hostname = map.value(QLatin1String(USER_FILE_HOSTNAME)).toString();
+ if (!hostname.isEmpty() && hostname != QHostInfo::localHostName()) {
+ // Ask the user
+ // TODO tr, casing check
+ QMessageBox msgBox(QMessageBox::Question,
+ QApplication::translate("ProjectExplorer::UserFileAccessor",
+ "Project Settings File from a different Host?"),
+ QApplication::translate("ProjectExplorer::UserFileAccessor",
+ "Qt Creator has found a .user settings file from a host %1. "
+ "The hostname for this computer is %2. \n\n"
+ "The .user settings files contain machine specific settings. "
+ "They should not be copied to a different environment. \n\n"
+ "Still load the settigns file?").arg(hostname, QHostInfo::localHostName()),
+ QMessageBox::Yes | QMessageBox::No,
+ Core::ICore::instance()->mainWindow());
+ msgBox.setDefaultButton(QMessageBox::No);
+ msgBox.setEscapeButton(QMessageBox::No);
+ int result = msgBox.exec();
+ if (result == QMessageBox::No)
+ return QVariantMap();
+ }
+
// Do we need to do a update?
if (fileVersion != m_lastVersion + 1) {
map.insert(QLatin1String(WAS_UPDATED), true);
writer.saveValue(i.key(), i.value());
writer.saveValue(QLatin1String(USER_FILE_VERSION), m_lastVersion + 1);
+ writer.saveValue(QLatin1String(USER_FILE_HOSTNAME), QHostInfo::localHostName());
return writer.save(fileNameFor(project->file()->fileName()), "QtCreatorProject");
}
-<plugin name="QmlDesigner" version="1.3.84" compatVersion="1.3.84" experimental="true">
+<plugin name="QmlDesigner" version="2.0.80" compatVersion="2.0.80" experimental="true">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Visual Designer for QML files.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="QmlJSEditor" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="QmlJSEditor" version="2.0.80"/>
</dependencyList>
</plugin>
ModelNode resolveToModelNode() const;
AbstractProperty resolveToProperty() const;
+ bool isList() const;
+ QList<ModelNode> resolveToModelNodeList() const;
protected:
BindingProperty(const QString &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view);
QString scopeAndName(const QString &combiner = QString("::")) const;
QList<QString> elementNames() const;
int elementValue(const QString &enumeratorName) const;
+ QString valueToString(int value) const;
private:
void setScope(const QString &scope);
bool isAncestorOf(const QmlObjectNode &objectNode) const;
+ static QVariant instanceValue(const ModelNode &modelNode, const QString &name);
+
protected:
NodeInstance nodeInstance() const;
QmlObjectNode nodeForInstance(const NodeInstance &instance) const;
class QmlModelView;
class QmlModelStateGroup;
+class QmlObjectNode;
class CORESHARED_EXPORT QmlModelState : public QmlModelNodeFacade
{
void removePropertyChanges(const ModelNode &node);
bool affectsModelNode(const ModelNode &node) const;
+ QList<QmlObjectNode> allAffectedNodes() const;
QString name() const;
void setName(const QString &name);
bool isValid() const;
return m_data->elements.value(unscoped, -1);
}
+QString EnumeratorMetaInfo::valueToString(int value) const
+{
+ return m_data->elements.key(value);
+}
+
void EnumeratorMetaInfo::setScope(const QString &scope)
{
Q_ASSERT(!scope.isEmpty());
<element name="NonModal" value="0" />
<element name="ApplicationModal" value="2" />
</enumerator>
+ <enumerator name="Type" scope="QEasingCurve">
+ <element name="Linear" value="0" />
+ <element name="InQuad" value="1" />
+ <element name="OutQuad" value="2" />
+ <element name="InOutQuad" value="3" />
+ <element name="OutInQuad" value="4" />
+ <element name="InCubic" value="5" />
+ <element name="OutCubic" value="6" />
+ <element name="InOutCubic" value="7" />
+ <element name="OutInCubic" value="8" />
+ <element name="InQuart" value="9" />
+ <element name="OutQuart" value="10" />
+ <element name="InOutQuart" value="11" />
+ <element name="OutInQuart" value="12" />
+ <element name="InQuint" value="13" />
+ <element name="OutQuint" value="14" />
+ <element name="InOutQuint" value="15" />
+ <element name="OutInQuint" value="16" />
+ <element name="InSine" value="17" />
+ <element name="OutSine" value="18" />
+ <element name="InOutSine" value="19" />
+ <element name="OutInSine" value="20" />
+ <element name="InExpo" value="21" />
+ <element name="OutExpo" value="22" />
+ <element name="InOutExpo" value="23" />
+ <element name="OutInExpo" value="24" />
+ <element name="InCirc" value="25" />
+ <element name="OutCirc" value="26" />
+ <element name="InOutCirc" value="27" />
+ <element name="OutInCirc" value="28" />
+ <element name="InElastic" value="29" />
+ <element name="OutElastic" value="30" />
+ <element name="InOutElastic" value="31" />
+ <element name="OutInElastic" value="32" />
+ <element name="InBack" value="33" />
+ <element name="OutBack" value="34" />
+ <element name="InOutBack" value="35" />
+ <element name="OutInBack" value="36" />
+ <element name="InBounce" value="37" />
+ <element name="OutBounce" value="38" />
+ <element name="InOutBounce" value="39" />
+ <element name="OutInBounce" value="40" />
+ <element name="InCurve" value="41" />
+ <element name="OutCurve" value="42" />
+ <element name="SineCurve" value="43" />
+ <element name="CosineCurve" value="44" />
+ <element name="Custom" value="45" />
+ <element name="NCurveTypes" value="46" />
+ </enumerator>
<flag name="Alignment" scope="Qt">
<element name="AlignLeft" value="1" />
<element name="AlignTrailing" value="2" />
parseQmlTypes();
parseNonQmlTypes();
- parseValueTypes();
parseXmlFiles();
+ parseValueTypes();
m_isInitialized = true;
}
propertyInfo.setFlagType(qProperty.isFlagType());
if (propertyInfo.isEnumType()) {
- EnumeratorMetaInfo enumerator;
-
QMetaEnum qEnumerator = qProperty.enumerator();
+ EnumeratorMetaInfo enumerator = m_q->addEnumerator(qEnumerator.scope(), qEnumerator.name());
+
enumerator.setValid(qEnumerator.isValid());
enumerator.setIsFlagType(qEnumerator.isFlag());
enumerator.setScope(qEnumerator.scope());
}
propertyInfo.setEnumerator(enumerator);
+
}
nodeMetaInfo.addProperty(propertyInfo);
<< "QRectF"
<< "QSize"
<< "QSizeF"
- << "QVector3D";
+ << "QVector3D"
+ << "QEasingCurve";
foreach (const QString &type, valueTypes) {
NodeMetaInfo nodeMetaInfo(*m_q);
propertyInfo.setType("int");
} else if (type == ("QRect")) {
propertyInfo.setType("int");
+ } else if (type == ("QEasingCurve")) {
+ if (propertyName == "type") {
+ propertyInfo.setEnumType("true");
+ propertyInfo.setType("QEasingCurve::Type");
+ propertyInfo.setEnumerator(m_q->enumerator("QEasingCurve::Type"));
+ }
}
propertyInfo.setValid(true);
propertyInfo.setReadable(true);
*/
PropertyMetaInfo AbstractProperty::metaInfo() const
{
- return ModelNode(m_internalNode, m_model.data(), view()).metaInfo().property(m_propertyName);
+ return ModelNode(m_internalNode, m_model.data(), view()).metaInfo().property(m_propertyName, true);
}
/*!
return resolveBinding(expression(), parentModelNode(), view());
}
+static inline QStringList commaSeparatedSimplifiedStringList(const QString &string)
+{
+ QStringList stringList = string.split(QLatin1String(","));
+ QStringList simpleList;
+ foreach (const QString &simpleString, stringList)
+ simpleList.append(simpleString.simplified());
+ return simpleList;
+}
+
+
AbstractProperty BindingProperty::resolveToProperty() const
{
if (!isValid())
return AbstractProperty();
}
+bool BindingProperty::isList() const
+{
+ if (!isValid())
+ throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
+
+ return expression().startsWith('[') && expression().endsWith(']');
+}
+
+QList<ModelNode> BindingProperty::resolveToModelNodeList() const
+{
+ QList<ModelNode> returnList;
+ if (!isValid())
+ throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
+ if (isList()) {
+ QString string = expression();
+ string.chop(1);
+ string.remove(0, 1);
+ QStringList simplifiedList = commaSeparatedSimplifiedStringList(string);
+ foreach (const QString &nodeId, simplifiedList) {
+ ModelNode modelNode = view()->modelNodeForId(nodeId);
+ if (modelNode.isValid())
+ returnList.append(modelNode);
+ }
+ }
+ return returnList;
+}
+
void BindingProperty::setDynamicTypeNameAndExpression(const QString &typeName, const QString &expression)
{
*/
QVariant QmlObjectNode::instanceValue(const QString &name) const
-{
- Q_ASSERT(qmlModelView()->hasInstanceForModelNode(modelNode()));
- return qmlModelView()->instanceForModelNode(modelNode()).property(name);
+{
+ return instanceValue(modelNode(), name);
}
return modelNode().isAncestorOf(objectNode.modelNode());
}
+QVariant QmlObjectNode::instanceValue(const ModelNode &modelNode, const QString &name)
+{
+ QmlModelView *modelView = qobject_cast<QmlModelView*>(modelNode.view());
+ if (!modelView)
+ throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
+ Q_ASSERT(modelView->hasInstanceForModelNode(modelNode));
+ return modelView->instanceForModelNode(modelNode).property(name);
+}
+
NodeInstance QmlObjectNode::nodeInstance() const
{
return qmlModelView()->nodeInstanceView()->instanceForNode(modelNode());
{
//### exception if not valid
+ if (!isValid())
+ throw new InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__);
+
if (isBaseState())
return;
return !stateOperations(node).isEmpty();
}
+QList<QmlObjectNode> QmlModelState::allAffectedNodes() const
+{
+ QList<QmlObjectNode> returnList;
+
+ foreach (const ModelNode &childNode, modelNode().nodeListProperty("changes").toModelNodeList()) {
+ //### exception if not valid QmlModelStateOperation
+ if (QmlModelStateOperation(childNode).isValid() &&
+ !returnList.contains(QmlModelStateOperation(childNode).target()))
+ returnList.append(QmlModelStateOperation(childNode).target());
+ }
+ return returnList;
+}
+
QString QmlModelState::name() const
{
if (isBaseState())
#include <QSize>
#include <QSizeF>
#include <QVector3D>
+#include <QEasingCurve>
#include <QMetaProperty>
namespace QmlDesigner {
return VariantParser(QVariant(QRectF()));
if (type == "QVector3D")
return VariantParser(QVariant(QVector3D()));
+ if (type == "QEasingCurve")
+ return VariantParser(QVariant(QEasingCurve()));
return VariantParser(QVariant());
}
if (type == "QRectF")
m_valueType = QDeclarativeValueTypeFactory::valueType(QVariant::RectF);
if (type == "QVector3D")
- m_valueType = QDeclarativeValueTypeFactory::valueType(QVariant::Vector3D);
+ m_valueType = QDeclarativeValueTypeFactory::valueType(QVariant::Vector3D);
+ if (type == "QEasingCurve")
+ m_valueType = QDeclarativeValueTypeFactory::valueType(QVariant::EasingCurve);
}
bool VariantParser::isValid()
-<plugin name="QmlInspector" version="1.3.84" compatVersion="1.3.84">
+<plugin name="QmlInspector" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Debugger for QML files</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="QmlProjectManager" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="CppTools" version="1.3.84"/>
- <dependency name="CppEditor" version="1.3.84"/>
- <dependency name="Debugger" version="1.3.84"/>
- <dependency name="QmlJSEditor" version="1.3.84"/>
+ <dependency name="QmlProjectManager" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="CppTools" version="2.0.80"/>
+ <dependency name="CppEditor" version="2.0.80"/>
+ <dependency name="Debugger" version="2.0.80"/>
+ <dependency name="QmlJSEditor" version="2.0.80"/>
</dependencyList>
</plugin>
// *************************************************************************
// PropertiesViewItem
// *************************************************************************
-PropertiesViewItem::PropertiesViewItem(QTreeWidget *widget, Type type)
- : QTreeWidgetItem(widget), type(type)
+PropertiesViewItem::PropertiesViewItem(QTreeWidget *widget, int type)
+ : QTreeWidgetItem(widget, type), m_disabled(false)
{
+
}
-PropertiesViewItem::PropertiesViewItem(QTreeWidgetItem *parent, Type type)
- : QTreeWidgetItem(parent), type(type)
+PropertiesViewItem::PropertiesViewItem(QTreeWidgetItem *parent, int type)
+ : QTreeWidgetItem(parent, type), m_disabled(false)
{
}
QVariant PropertiesViewItem::data (int column, int role) const
{
- if (column == 1) {
- if (role == Qt::ForegroundRole) {
- bool canEdit = data(0, CanEditRole).toBool();
- return canEdit ? qApp->palette().color(QPalette::Foreground) : qApp->palette().color(QPalette::Disabled, QPalette::Foreground);
- }
+ if (role == Qt::ForegroundRole) {
+ bool makeDisabledColor = m_disabled;
+ if (column == 1 && !data(0, CanEditRole).toBool())
+ makeDisabledColor = true;
+ return makeDisabledColor ? qApp->palette().color(QPalette::Disabled, QPalette::Foreground) : qApp->palette().color(QPalette::Foreground);
}
+
return QTreeWidgetItem::data(column, role);
}
{
return data(0, ObjectIdStringRole).toString();
}
+void PropertiesViewItem::setWatchingDisabled(bool disabled)
+{
+ m_disabled = disabled;
+}
+bool PropertiesViewItem::isWatchingDisabled() const
+{
+ return m_disabled;
+}
} // Internal
} // Qml
{
public:
enum Type {
- BindingType,
- OtherType,
- ClassType,
+ BindingType = QTreeWidgetItem::UserType,
+ OtherType = QTreeWidgetItem::UserType + 1,
+ ClassType = QTreeWidgetItem::UserType + 2
};
enum DataRoles {
- CanEditRole = Qt::UserRole + 1,
- ObjectIdStringRole = Qt::UserRole + 50,
- ClassDepthRole = Qt::UserRole + 51
+ CanEditRole = Qt::UserRole,
+ ObjectIdStringRole = Qt::UserRole + 1,
+ ClassDepthRole = Qt::UserRole + 2
};
- PropertiesViewItem(QTreeWidget *widget, Type type = OtherType);
- PropertiesViewItem(QTreeWidgetItem *parent, Type type = OtherType);
+ PropertiesViewItem(QTreeWidget *widget, int type = OtherType);
+ PropertiesViewItem(QTreeWidgetItem *parent, int type = OtherType);
QVariant data (int column, int role) const;
void setData (int column, int role, const QVariant & value);
-
+ void setWatchingDisabled(bool disabled);
+ bool isWatchingDisabled() const;
QDeclarativeDebugPropertyReference property;
- Type type;
+
private:
QString objectIdString() const;
+ bool m_disabled;
};
setObject(obj);
}
-void ObjectPropertiesView::setPropertyValue(PropertiesViewItem *item, const QVariant &value, bool makeGray)
+void ObjectPropertiesView::setPropertyValue(PropertiesViewItem *item, const QVariant &value, bool isDisabled)
{
+ item->setWatchingDisabled(isDisabled);
+
if (value.type() == QVariant::List || value.type() == QVariant::StringList) {
PropertiesViewItem *bindingItem = static_cast<PropertiesViewItem*>(item->takeChild(item->childCount() - 1));
- if (bindingItem && bindingItem->type != PropertiesViewItem::BindingType) {
+ if (bindingItem && bindingItem->type() != PropertiesViewItem::BindingType) {
delete bindingItem;
bindingItem = 0;
}
PropertiesViewItem *child;
for (int i=0; i<variants.count(); ++i) {
child = new PropertiesViewItem(item);
- setPropertyValue(child, variants[i], makeGray);
+ setPropertyValue(child, variants[i], isDisabled);
}
if (bindingItem)
item->setExpanded(true);
}
- if (makeGray) {
- for (int i=0; i<m_tree->columnCount(); ++i)
- item->setForeground(i, Qt::gray);
- }
}
QString ObjectPropertiesView::propertyBaseClass(const QDeclarativeDebugObjectReference &object, const QDeclarativeDebugPropertyReference &property, int &depth)
{
m_clickedItem = m_tree->itemAt(QPoint(event->pos().x(),
event->pos().y() - m_tree->header()->height()));
+
if (!m_clickedItem)
return;
PropertiesViewItem *propItem = static_cast<PropertiesViewItem *>(m_clickedItem);
+ bool isWatchableItem = propItem->type() != PropertiesViewItem::ClassType &&
+ propItem->type() != PropertiesViewItem::BindingType;
+
QMenu menu;
- if (!isWatched(m_clickedItem)) {
- m_addWatchAction->setText(tr("Watch expression '%1'").arg(propItem->property.name()));
- menu.addAction(m_addWatchAction);
- } else {
- menu.addAction(m_removeWatchAction);
+ if (isWatchableItem) {
+ if (!isWatched(m_clickedItem)) {
+ m_addWatchAction->setText(tr("Watch expression '%1'").arg(propItem->property.name()));
+ m_addWatchAction->setDisabled(propItem->isWatchingDisabled());
+ menu.addAction(m_addWatchAction);
+ } else {
+ menu.addAction(m_removeWatchAction);
+ m_addWatchAction->setDisabled(propItem->isWatchingDisabled());
+ }
}
menu.addSeparator();
+
if (m_showUnwatchableProperties)
m_toggleUnwatchablePropertiesAction->setText(tr("Hide unwatchable properties"));
else
void setObject(const QDeclarativeDebugObjectReference &object);
bool isWatched(QTreeWidgetItem *item);
void setWatched(const QString &property, bool watched);
- void setPropertyValue(PropertiesViewItem *item, const QVariant &value, bool makeGray);
+ void setPropertyValue(PropertiesViewItem *item, const QVariant &value, bool isDisabled);
QDeclarativeEngineDebug *m_client;
QDeclarativeDebugObjectQuery *m_query;
-<plugin name="QmlJSEditor" version="1.3.84" compatVersion="1.3.84">
+<plugin name="QmlJSEditor" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Editor for QML and JavaScript.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
</dependencyList>
</plugin>
{
m_currentBlockParentheses.reserve(20);
m_braceDepth = 0;
+ m_foldingIndent = 0;
}
Highlighter::~Highlighter()
break;
case Token::LeftParenthesis:
- onOpeningParenthesis('(', token.offset);
+ onOpeningParenthesis('(', token.offset, index == 0);
break;
case Token::RightParenthesis:
- onClosingParenthesis(')', token.offset);
+ onClosingParenthesis(')', token.offset, index == tokens.size()-1);
break;
case Token::LeftBrace:
- onOpeningParenthesis('{', token.offset);
+ onOpeningParenthesis('{', token.offset, index == 0);
break;
case Token::RightBrace:
- onClosingParenthesis('}', token.offset);
+ onClosingParenthesis('}', token.offset, index == tokens.size()-1);
break;
case Token::LeftBracket:
- onOpeningParenthesis('[', token.offset);
+ onOpeningParenthesis('[', token.offset, index == 0);
break;
case Token::RightBracket:
- onClosingParenthesis(']', token.offset);
+ onClosingParenthesis(']', token.offset, index == tokens.size()-1);
break;
case Token::Identifier: {
setFormat(previousTokenEnd, text.length() - previousTokenEnd, m_formats[VisualWhitespace]);
- int firstNonSpace = 0;
- if (! tokens.isEmpty())
- firstNonSpace = tokens.first().offset;
-
setCurrentBlockState(m_scanner.state());
- onBlockEnd(m_scanner.state(), firstNonSpace);
+ onBlockEnd(m_scanner.state());
}
bool Highlighter::maybeQmlKeyword(const QStringRef &text) const
{
m_currentBlockParentheses.clear();
m_braceDepth = 0;
+ m_foldingIndent = 0;
+ if (TextEditor::TextBlockUserData *userData = TextEditor::BaseTextDocumentLayout::testUserData(currentBlock())) {
+ userData->setFoldingIndent(0);
+ userData->setFoldingStartIncluded(false);
+ userData->setFoldingEndIncluded(false);
+ }
int state = 0;
int previousState = previousBlockState();
state = previousState & 0xff;
m_braceDepth = previousState >> 8;
}
+ m_foldingIndent = m_braceDepth;
return state;
}
-void Highlighter::onBlockEnd(int state, int firstNonSpace)
+void Highlighter::onBlockEnd(int state)
{
typedef TextEditor::TextBlockUserData TextEditorBlockData;
setCurrentBlockState((m_braceDepth << 8) | state);
-
- // Set block data parentheses. Force creation of block data unless empty
- TextEditorBlockData *blockData = 0;
-
- if (QTextBlockUserData *userData = currentBlockUserData())
- blockData = static_cast<TextEditorBlockData *>(userData);
-
- if (!blockData && !m_currentBlockParentheses.empty()) {
- blockData = new TextEditorBlockData;
- setCurrentBlockUserData(blockData);
- }
- if (blockData) {
- blockData->setParentheses(m_currentBlockParentheses);
- blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse);
- blockData->setCollapseMode(TextEditor::TextBlockUserData::NoCollapse);
- }
- if (!m_currentBlockParentheses.isEmpty()) {
- QTC_ASSERT(blockData, return);
- int collapse = Parenthesis::collapseAtPos(m_currentBlockParentheses);
- if (collapse >= 0) {
- if (collapse == firstNonSpace)
- blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseThis);
- else
- blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseAfter);
- }
- if (Parenthesis::hasClosingCollapse(m_currentBlockParentheses))
- blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse);
- }
+ TextEditor::BaseTextDocumentLayout::setParentheses(currentBlock(), m_currentBlockParentheses);
+ TextEditor::BaseTextDocumentLayout::setFoldingIndent(currentBlock(), m_foldingIndent);
}
-void Highlighter::onOpeningParenthesis(QChar parenthesis, int pos)
+void Highlighter::onOpeningParenthesis(QChar parenthesis, int pos, bool atStart)
{
- if (parenthesis == QLatin1Char('{') || parenthesis == QLatin1Char('['))
+ if (parenthesis == QLatin1Char('{') || parenthesis == QLatin1Char('[')) {
++m_braceDepth;
+ // if a folding block opens at the beginning of a line, treat the entire line
+ // as if it were inside the folding block
+ if (atStart)
+ TextEditor::BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true);
+ }
m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Opened, parenthesis, pos));
}
-void Highlighter::onClosingParenthesis(QChar parenthesis, int pos)
+void Highlighter::onClosingParenthesis(QChar parenthesis, int pos, bool atEnd)
{
- if (parenthesis == QLatin1Char('}') || parenthesis == QLatin1Char(']'))
+ if (parenthesis == QLatin1Char('}') || parenthesis == QLatin1Char(']')) {
--m_braceDepth;
+ if (atEnd)
+ TextEditor::BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true);
+ else
+ m_foldingIndent = qMin(m_braceDepth, m_foldingIndent); // folding indent is the minimum brace depth of a block
+ }
m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Closed, parenthesis, pos));
}
virtual void highlightBlock(const QString &text);
int onBlockStart();
- void onBlockEnd(int state, int firstNonSpace);
+ void onBlockEnd(int state);
// The functions are notified whenever parentheses are encountered.
// Custom behaviour can be added, for example storing info for indenting.
- void onOpeningParenthesis(QChar parenthesis, int pos);
- void onClosingParenthesis(QChar parenthesis, int pos);
+ void onOpeningParenthesis(QChar parenthesis, int pos, bool atStart);
+ void onClosingParenthesis(QChar parenthesis, int pos, bool atEnd);
bool maybeQmlKeyword(const QStringRef &text) const;
bool maybeQmlBuiltinType(const QStringRef &text) const;
bool m_qmlEnabled;
int m_braceDepth;
+ int m_foldingIndent;
QmlJS::Scanner m_scanner;
Parentheses m_currentBlockParentheses;
-<plugin name="QmlProjectManager" version="1.3.84" compatVersion="1.3.84">
+<plugin name="QmlProjectManager" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Qt Quick support</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="QmlJSEditor" version="1.3.84"/>
- <dependency name="Debugger" version="1.3.84" />
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="QmlJSEditor" version="2.0.80"/>
+ <dependency name="Debugger" version="2.0.80" />
</dependencyList>
</plugin>
return false;
}
+void QmlProjectFile::rename(const QString &newName)
+{
+ // Can't happen...
+ Q_UNUSED(newName);
+ Q_ASSERT(false);
+}
+
QString QmlProjectFile::fileName() const
{
return m_fileName;
virtual bool save(const QString &fileName = QString());
virtual QString fileName() const;
+ virtual void rename(const QString &newName);
virtual QString defaultPath() const;
virtual QString suggestedFileName() const;
return true;
}
-QList<ProjectExplorer::ProjectNode::ProjectAction> QmlProjectNode::supportedActions() const
+QList<ProjectExplorer::ProjectNode::ProjectAction> QmlProjectNode::supportedActions(Node *node) const
{
+ Q_UNUSED(node);
QList<ProjectAction> actions;
actions.append(AddFile);
return actions;
virtual bool hasBuildTargets() const;
- virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions() const;
+ virtual QList<ProjectExplorer::ProjectNode::ProjectAction> supportedActions(Node *node) const;
virtual bool addSubProjects(const QStringList &proFilePaths);
virtual bool removeSubProjects(const QStringList &proFilePaths);
namespace QmlProjectManager {
namespace Internal {
-QmlRunControl::QmlRunControl(QmlProjectRunConfiguration *runConfiguration, bool debugMode)
- : RunControl(runConfiguration), m_debugMode(debugMode)
+QmlRunControl::QmlRunControl(QmlProjectRunConfiguration *runConfiguration, QString mode)
+ : RunControl(runConfiguration, mode)
{
ProjectExplorer::Environment environment = ProjectExplorer::Environment::systemEnvironment();
- if (debugMode)
+ if (runMode() == ProjectExplorer::Constants::DEBUGMODE)
environment.set(QmlProjectManager::Constants::E_QML_DEBUG_SERVER_PORT, QString::number(runConfiguration->debugServerPort()));
m_applicationLauncher.setEnvironment(environment.toStringList());
void QmlRunControl::slotAddToOutputWindow(const QString &line, bool onStdErr)
{
- if (m_debugMode && line.startsWith("QDeclarativeDebugServer: Waiting for connection")) {
+ if (runMode() == ProjectExplorer::Constants::DEBUGMODE && line.startsWith("QDeclarativeDebugServer: Waiting for connection")) {
Core::ICore *core = Core::ICore::instance();
core->modeManager()->activateMode(Debugger::Constants::MODE_DEBUG);
}
const QString &mode)
{
QTC_ASSERT(canRun(runConfiguration, mode), return 0);
- return new QmlRunControl(qobject_cast<QmlProjectRunConfiguration *>(runConfiguration),
- mode == ProjectExplorer::Constants::DEBUGMODE);
+ return new QmlRunControl(qobject_cast<QmlProjectRunConfiguration *>(runConfiguration), mode);
}
QString QmlRunControlFactory::displayName() const
namespace Internal {
-class QmlRunControl : public ProjectExplorer::RunControl {
+class QmlRunControl : public ProjectExplorer::RunControl
+{
Q_OBJECT
public:
- explicit QmlRunControl(QmlProjectRunConfiguration *runConfiguration, bool debugMode);
+ explicit QmlRunControl(QmlProjectRunConfiguration *runConfiguration, QString mode);
virtual ~QmlRunControl ();
// RunControl
QString m_executable;
QStringList m_commandLineArguments;
- bool m_debugMode;
};
class QmlRunControlFactory : public ProjectExplorer::IRunControlFactory {
-<plugin name="Qt4ProjectManager" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Qt4ProjectManager" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Provides project type for Qt 4 pro files and tools.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="CppTools" version="1.3.84"/>
- <dependency name="CppEditor" version="1.3.84"/>
- <dependency name="Help" version="1.3.84"/>
- <dependency name="Designer" version="1.3.84"/>
- <dependency name="Debugger" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="CppTools" version="2.0.80"/>
+ <dependency name="CppEditor" version="2.0.80"/>
+ <dependency name="Help" version="2.0.80"/>
+ <dependency name="Designer" version="2.0.80"/>
+ <dependency name="Debugger" version="2.0.80"/>
</dependencyList>
</plugin>
// This is a HACK, remove once the symbian make generator supports
// shadow building
arguments << QLatin1String("-after")
- << QLatin1String("OBJECTS_DIR=.obj")
- << QLatin1String("MOC_DIR=.moc")
- << QLatin1String("UI_DIR=.ui")
- << QLatin1String("RCC_DIR=.rcc");
+ << QLatin1String("OBJECTS_DIR=obj")
+ << QLatin1String("MOC_DIR=moc")
+ << QLatin1String("UI_DIR=ui")
+ << QLatin1String("RCC_DIR=rcc");
}
// Find out what flags we pass on to qmake
#include "maemotoolchain.h"
#include <qt4projectmanager/qt4buildconfiguration.h>
+#include <qt4projectmanager/qt4project.h>
+#include <qt4projectmanager/qt4target.h>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
QString MaemoPackageContents::remoteExecutableFilePath() const
{
if (m_remoteExecutableFilePath.isEmpty()) {
- m_remoteExecutableFilePath = QLatin1String("/usr/local/bin/")
- + m_packageStep->executableFileName();
+ const Qt4ProjectType projectType
+ = m_packageStep->qt4BuildConfiguration()->qt4Target()->qt4Project()
+ ->rootProjectNode()->projectType();
+ const QString remoteDir = projectType == LibraryTemplate
+ ? QLatin1String("/usr/local/lib/")
+ : QLatin1String("/usr/local/bin/");
+ m_remoteExecutableFilePath
+ = remoteDir + m_packageStep->executableFileName();
}
return m_remoteExecutableFilePath;
}
->qt4Project()->rootProjectNode()->targetInformation();
if (!ti.valid)
return QString();
-
return QDir::toNativeSeparators(QDir::cleanPath(ti.workingDir
- + QLatin1Char('/') + ti.target));
+ + QLatin1Char('/') + executableFileName()));
}
QString MaemoPackageCreationStep::executableFileName() const
{
- return QFileInfo(localExecutableFilePath()).fileName();
+ const Qt4Project * const project
+ = qt4BuildConfiguration()->qt4Target()->qt4Project();
+ const TargetInformation &ti
+ = project->rootProjectNode()->targetInformation();
+ if (!ti.valid)
+ return QString();
+
+ return project->rootProjectNode()->projectType() == LibraryTemplate
+ ? QLatin1String("lib") + ti.target + QLatin1String(".so")
+ : ti.target;
}
const MaemoToolChain *MaemoPackageCreationStep::maemoToolChain() const
QString localExecutableFilePath() const;
QString executableFileName() const;
MaemoPackageContents *packageContents() const { return m_packageContents; }
+ const Qt4BuildConfiguration *qt4BuildConfiguration() const;
private:
MaemoPackageCreationStep(ProjectExplorer::BuildConfiguration *buildConfig,
bool createPackage();
bool runCommand(QProcess &proc, const QString &command);
- const Qt4BuildConfiguration *qt4BuildConfiguration() const;
const MaemoToolChain *maemoToolChain() const;
QString maddeRoot() const;
QString targetRoot() const;
return m_devConfig;
}
-const QString MaemoRunConfiguration::sshCmd() const
-{
- return cmd(QString::fromLocal8Bit("ssh"));
-}
-
-const QString MaemoRunConfiguration::scpCmd() const
-{
- return cmd(QString::fromLocal8Bit("scp"));
-}
-
-const QString MaemoRunConfiguration::cmd(const QString &cmdName) const
-{
- QString command(cmdName);
-#ifdef Q_OS_WIN
- command = maddeRoot() + QLatin1String("/bin/") + command
- + QLatin1String(".exe");
-#endif
- return command;
-}
-
const MaemoToolChain *MaemoRunConfiguration::toolchain() const
{
Qt4BuildConfiguration *qt4bc(activeQt4BuildConfiguration());
MaemoDeviceConfig deviceConfig() const;
QString runtimeGdbServerPort() const;
+<<<<<<< HEAD
+=======
const QString sshCmd() const;
const QString scpCmd() const;
+>>>>>>> origin/2.0
const QString gdbCmd() const;
const QString dumperLib() const;
private:
void init();
+<<<<<<< HEAD
+=======
const QString cmd(const QString &cmdName) const;
+>>>>>>> origin/2.0
const MaemoToolChain *toolchain() const;
bool fileNeedsDeployment(const QString &path, const QDateTime &lastDeployed) const;
void addDeployTimesToMap(const QString &key,
#include <extensionsystem/pluginmanager.h>
#include <projectexplorer/toolchain.h>
#include <utils/qtcassert.h>
+#include <projectexplorer/projectexplorerconstants.h>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
namespace Qt4ProjectManager {
namespace Internal {
+#define USE_GDBSERVER
+
using ProjectExplorer::RunConfiguration;
using ProjectExplorer::ToolChain;
-AbstractMaemoRunControl::AbstractMaemoRunControl(RunConfiguration *rc)
- : RunControl(rc)
+AbstractMaemoRunControl::AbstractMaemoRunControl(RunConfiguration *rc, QString mode)
+ : RunControl(rc, mode)
, m_runConfig(qobject_cast<MaemoRunConfiguration *>(rc))
, m_devConfig(m_runConfig ? m_runConfig->deviceConfig() : MaemoDeviceConfig())
{
emit appendMessage(this, tr("Initial cleanup canceled by user."), false);
emit finished();
} else if (m_initialCleaner->hasError()) {
- handleError(tr("Error running initial cleanup: %1.")
+ handleError(tr("Error running initial cleanup: %1")
.arg(m_initialCleaner->error()));
emit finished();
} else {
m_needsInstall = true;
} else {
m_needsInstall = false;
- }
+ }
if (forDebugging
&& m_runConfig->debuggingHelpersNeedDeployment(m_devConfig.server.host)) {
const QFileInfo &info(m_runConfig->dumperLib());
emit appendMessage(this,
tr("Remote execution canceled due to user request."),
false);
- } else if (m_sshRunner->hasError()) {
+ } else if (m_sshRunner && m_sshRunner->hasError()) {
emit appendMessage(this, tr("Error running remote process: %1")
.arg(m_sshRunner->error()),
true);
MaemoRunControl::MaemoRunControl(RunConfiguration *runConfiguration)
- : AbstractMaemoRunControl(runConfiguration)
+ : AbstractMaemoRunControl(runConfiguration, ProjectExplorer::Constants::RUNMODE)
{
}
MaemoDebugRunControl::MaemoDebugRunControl(RunConfiguration *runConfiguration)
- : AbstractMaemoRunControl(runConfiguration)
+ : AbstractMaemoRunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE)
, m_debuggerManager(ExtensionSystem::PluginManager::instance()
->getObject<Debugger::DebuggerManager>())
, m_startParams(new Debugger::DebuggerStartParameters)
{
QTC_ASSERT(m_debuggerManager != 0, return);
- m_startParams->startMode = Debugger::StartRemote;
+#ifdef USE_GDBSERVER
+ m_startParams->startMode = Debugger::AttachToRemote;
m_startParams->executable = executableOnHost();
+ m_startParams->debuggerCommand = m_runConfig->gdbCmd();
m_startParams->remoteChannel
= m_devConfig.server.host % QLatin1Char(':') % gdbServerPort();
m_startParams->remoteArchitecture = QLatin1String("arm");
+#else
+ m_startParams->startMode = Debugger::StartRemoteGdb;
+ m_startParams->executable = executableFilePathOnTarget();
+ m_startParams->debuggerCommand = QLatin1String("/usr/bin/gdb");
+ m_startParams->sshserver = m_devConfig.server;
+#endif
+ m_startParams->processArgs = m_runConfig->arguments();
m_startParams->sysRoot = m_runConfig->sysRoot();
m_startParams->toolChainType = ToolChain::GCC_MAEMO;
- m_startParams->debuggerCommand = m_runConfig->gdbCmd();
m_startParams->dumperLibrary = m_runConfig->dumperLib();
- m_startParams->remoteDumperLib = QString::fromLocal8Bit("%1/%2")
- .arg(remoteDir()).arg(QFileInfo(m_runConfig->dumperLib()).fileName());
+ m_startParams->remoteDumperLib = remoteDir().toUtf8() + '/'
+ + QFileInfo(m_runConfig->dumperLib()).fileName().toUtf8();
connect(m_debuggerManager, SIGNAL(debuggingFinished()), this,
SLOT(debuggingFinished()), Qt::QueuedConnection);
connect(m_debuggerManager, SIGNAL(applicationOutputAvailable(QString, bool)),
- this, SLOT(debuggerOutput(QString)), Qt::QueuedConnection);
+ this,
+#ifdef USE_GDBSERVER
+ SLOT(debuggerOutput(QString))
+#else
+ SLOT(handleRemoteOutput(QString))
+#endif
+ , Qt::QueuedConnection);
}
MaemoDebugRunControl::~MaemoDebugRunControl()
disconnect(SIGNAL(addToOutputWindow(RunControl*,QString, bool)));
disconnect(SIGNAL(addToOutputWindowInline(RunControl*,QString, bool)));
stop();
- debuggingFinished();
}
void MaemoDebugRunControl::startInternal()
.arg(executableFilePathOnTarget()).arg(targetCmdLineSuffix());
}
+void MaemoDebugRunControl::startExecution()
+{
+#ifdef USE_GDBSERVER
+ AbstractMaemoRunControl::startExecution();
+#else
+ startDebugging();
+#endif
+}
+
void MaemoDebugRunControl::handleRemoteOutput(const QString &output)
{
+#ifdef USE_GDBSERVER
if (!m_debuggingStarted) {
m_debuggingStarted = true;
startDebugging();
}
+#endif
emit addToOutputWindowInline(this, output, false);
}
void MaemoDebugRunControl::debuggingFinished()
{
+#ifdef USE_GDBSERVER
AbstractMaemoRunControl::stopRunning(true);
+#else
+ AbstractMaemoRunControl::handleRunThreadFinished();
+#endif
}
void MaemoDebugRunControl::debuggerOutput(const QString &output)
Q_OBJECT
public:
- explicit AbstractMaemoRunControl(ProjectExplorer::RunConfiguration *runConfig);
+ explicit AbstractMaemoRunControl(ProjectExplorer::RunConfiguration *runConfig, QString mode);
virtual ~AbstractMaemoRunControl();
protected:
void startDeployment(bool forDebugging);
void deploy();
void stopRunning(bool forDebugging);
- void startExecution();
+ virtual void startExecution();
void handleError(const QString &errString);
const QString executableOnHost() const;
const QString executableFileName() const;
QString packageFilePath() const;
QString executableFilePathOnTarget() const;
+protected slots:
+ void handleRunThreadFinished();
+
private slots:
virtual void handleRemoteOutput(const QString &output)=0;
void handleInitialCleanupFinished();
void handleDeployThreadFinished();
- void handleRunThreadFinished();
void handleFileCopied();
protected:
private:
virtual void startInternal();
virtual void stopInternal();
+ virtual void startExecution();
virtual QString remoteCall() const;
QString gdbServerPort() const;
void MaemoSettingsWidget::deviceTypeChanged()
{
+ const QString name = currentConfig().name;
const MaemoDeviceConfig::DeviceType devType =
m_ui->deviceButton->isChecked()
? MaemoDeviceConfig::Physical
m_lastConfigSim = currentConfig();
currentConfig() = m_lastConfigHW;
}
+ currentConfig().name = name;
fillInValues();
}
bool MaemoSshRunner::runInternal()
{
createConnection();
- connect(m_connection.data(), SIGNAL(remoteOutput(QByteArray)),
- this, SLOT(handleRemoteOutput(QByteArray)));
+ connect(m_connection.data(), SIGNAL(remoteOutputAvailable()),
+ this, SLOT(handleRemoteOutput()));
m_endMarkerCount = 0;
m_promptEncountered = false;
if (!m_connection->start())
return !m_connection->hasError();
}
-void MaemoSshRunner::handleRemoteOutput(const QByteArray &curOutput)
+void MaemoSshRunner::handleRemoteOutput()
{
- const QByteArray output = m_potentialEndMarkerPrefix + curOutput;
+ const QByteArray output
+ = m_potentialEndMarkerPrefix + m_connection->waitForRemoteOutput(0);
// Wait for a prompt before sending the command.
if (!m_promptEncountered) {
private:
virtual bool runInternal();
- Q_SLOT void handleRemoteOutput(const QByteArray &output);
+ Q_SLOT void handleRemoteOutput();
static const QByteArray EndMarker;
const char * const SIGNMODE_KEY("Qt4ProjectManager.S60CreatePackageStep.SignMode");
const char * const CERTIFICATE_KEY("Qt4ProjectManager.S60CreatePackageStep.Certificate");
const char * const KEYFILE_KEY("Qt4ProjectManager.S60CreatePackageStep.Keyfile");
+ const char * const SMART_INSTALLER_KEY("Qt4ProjectManager.S60CreatorPackageStep.SmartInstaller");
}
// #pragma mark -- S60SignBuildStep
S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildConfiguration *bc) :
MakeStep(bc, QLatin1String(SIGN_BS_ID)),
- m_signingMode(SignSelf)
+ m_signingMode(SignSelf),
+ m_createSmartInstaller(false)
{
ctor_package();
}
MakeStep(bc, bs),
m_signingMode(bs->m_signingMode),
m_customSignaturePath(bs->m_customSignaturePath),
- m_customKeyPath(bs->m_customKeyPath)
+ m_customKeyPath(bs->m_customKeyPath),
+ m_createSmartInstaller(bs->m_createSmartInstaller)
{
ctor_package();
}
S60CreatePackageStep::S60CreatePackageStep(ProjectExplorer::BuildConfiguration *bc, const QString &id) :
MakeStep(bc, id),
- m_signingMode(SignSelf)
+ m_signingMode(SignSelf),
+ m_createSmartInstaller(false)
{
ctor_package();
}
map.insert(QLatin1String(SIGNMODE_KEY), (int)m_signingMode);
map.insert(QLatin1String(CERTIFICATE_KEY), m_customSignaturePath);
map.insert(QLatin1String(KEYFILE_KEY), m_customKeyPath);
+ map.insert(QLatin1String(SMART_INSTALLER_KEY), m_createSmartInstaller);
return map;
}
m_signingMode = (SigningMode)map.value(QLatin1String(SIGNMODE_KEY)).toInt();
m_customSignaturePath = map.value(QLatin1String(CERTIFICATE_KEY)).toString();
m_customKeyPath = map.value(QLatin1String(KEYFILE_KEY)).toString();
+ m_createSmartInstaller = map.value(QLatin1String(SMART_INSTALLER_KEY), false).toBool();
return MakeStep::fromMap(map);
}
ProjectExplorer::Environment environment = bc->environment();
setEnvironment(environment);
QStringList args;
- args << QLatin1String("sis");
+ if (m_createSmartInstaller)
+ args << QLatin1String("installer_sis");
+ else
+ args << QLatin1String("sis");
if (signingMode() == SignCustom) {
args << QLatin1String("QT_SIS_CERTIFICATE=") + QDir::toNativeSeparators(customSignaturePath())
<< QLatin1String("QT_SIS_KEY=") + QDir::toNativeSeparators(customKeyPath());
m_customKeyPath = path;
}
+bool S60CreatePackageStep::createsSmartInstaller() const
+{
+ return m_createSmartInstaller;
+}
+
+void S60CreatePackageStep::setCreatesSmartInstaller(bool value)
+{
+ m_createSmartInstaller = value;
+ static_cast<Qt4BuildConfiguration *>(buildConfiguration())->emitS60CreatesSmartInstallerChanged();
+}
+
// #pragma mark -- S60SignBuildStepFactory
S60CreatePackageStepFactory::S60CreatePackageStepFactory(QObject *parent) :
this, SLOT(updateFromUi()));
connect(m_ui.keyFilePath, SIGNAL(changed(QString)),
this, SLOT(updateFromUi()));
+ connect(m_ui.smartInstaller, SIGNAL(clicked()),
+ this, SLOT(updateFromUi()));
}
void S60CreatePackageStepConfigWidget::updateUi()
m_ui.keyFilePath->setEnabled(!selfSigned);
m_ui.signaturePath->setPath(m_signStep->customSignaturePath());
m_ui.keyFilePath->setPath(m_signStep->customKeyPath());
+ m_ui.smartInstaller->setChecked(m_signStep->createsSmartInstaller());
emit updateSummary();
}
: S60CreatePackageStep::SignCustom);
m_signStep->setCustomSignaturePath(m_ui.signaturePath->path());
m_signStep->setCustomKeyPath(m_ui.keyFilePath->path());
+ m_signStep->setCreatesSmartInstaller(m_ui.smartInstaller->isChecked());
updateUi();
}
.arg(m_signStep->customSignaturePath())
.arg(m_signStep->customKeyPath());
}
+ if (m_signStep->createsSmartInstaller())
+ return tr("<b>Create SIS Package:</b> %1, using Smart Installer").arg(text);
return tr("<b>Create SIS Package:</b> %1").arg(text);
}
void setCustomSignaturePath(const QString &path);
QString customKeyPath() const;
void setCustomKeyPath(const QString &path);
+ bool createsSmartInstaller() const;
+ void setCreatesSmartInstaller(bool value);
protected:
S60CreatePackageStep(ProjectExplorer::BuildConfiguration *bc, S60CreatePackageStep *bs);
SigningMode m_signingMode;
QString m_customSignaturePath;
QString m_customKeyPath;
+ bool m_createSmartInstaller;
};
class S60CreatePackageStepConfigWidget : public ProjectExplorer::BuildStepConfigWidget
<x>0</x>
<y>0</y>
<width>517</width>
- <height>95</height>
+ <height>108</height>
</rect>
</property>
<property name="windowTitle">
</layout>
</item>
<item>
+ <widget class="QCheckBox" name="smartInstaller">
+ <property name="text">
+ <string>Create Smart Installer package</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<class>Utils::PathChooser</class>
<extends>QWidget</extends>
<header location="global">utils/pathchooser.h</header>
+ <container>1</container>
+ <slots>
+ <signal>editingFinished()</signal>
+ <signal>browsingFinished()</signal>
+ </slots>
</customwidget>
</customwidgets>
<resources/>
#include "symbiandevicemanager.h"
#include "qt4buildconfiguration.h"
#include "qt4projectmanagerconstants.h"
+#include "s60createpackagestep.h"
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
S60DeviceRunConfiguration::S60DeviceRunConfiguration(Target *parent, const QString &proFilePath) :
RunConfiguration(parent, QLatin1String(S60_DEVICE_RC_ID)),
m_proFilePath(proFilePath),
+ m_activeBuildConfiguration(0),
#ifdef Q_OS_WIN
m_serialPortName(QLatin1String("COM5"))
#else
S60DeviceRunConfiguration::S60DeviceRunConfiguration(Target *target, S60DeviceRunConfiguration *source) :
RunConfiguration(target, source),
m_proFilePath(source->m_proFilePath),
+ m_activeBuildConfiguration(0),
m_serialPortName(source->m_serialPortName)
{
ctor();
connect(qt4Target()->qt4Project(), SIGNAL(proFileUpdated(Qt4ProjectManager::Internal::Qt4ProFileNode*)),
this, SLOT(proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode*)));
+ connect(qt4Target(), SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
+ this, SLOT(updateActiveBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
+ updateActiveBuildConfiguration(qt4Target()->activeBuildConfiguration());
}
void S60DeviceRunConfiguration::proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode *pro)
emit targetInformationChanged();
}
+void S60DeviceRunConfiguration::updateActiveBuildConfiguration(ProjectExplorer::BuildConfiguration *buildConfiguration)
+{
+ if (m_activeBuildConfiguration)
+ disconnect(m_activeBuildConfiguration, SIGNAL(s60CreatesSmartInstallerChanged()),
+ this, SIGNAL(targetInformationChanged()));
+ m_activeBuildConfiguration = buildConfiguration;
+ if (m_activeBuildConfiguration)
+ connect(m_activeBuildConfiguration, SIGNAL(s60CreatesSmartInstallerChanged()),
+ this, SIGNAL(targetInformationChanged()));
+}
+
S60DeviceRunConfiguration::~S60DeviceRunConfiguration()
{
}
return QDir::toNativeSeparators(localExecutable);
}
+bool S60DeviceRunConfiguration::runSmartInstaller() const
+{
+ BuildConfiguration *bc = target()->activeBuildConfiguration();
+ QTC_ASSERT(bc, return false);
+ QList<BuildStep *> steps = bc->steps(Build);
+ foreach (const BuildStep *step, steps) {
+ if (const S60CreatePackageStep *packageStep = qobject_cast<const S60CreatePackageStep *>(step)) {
+ return packageStep->createsSmartInstaller();
+ }
+ }
+ return false;
+}
+
QString S60DeviceRunConfiguration::signedPackage() const
{
TargetInformation ti = qt4Target()->qt4Project()->rootProjectNode()->targetInformation(m_proFilePath);
if (!ti.valid)
return QString();
- return ti.buildDir + QLatin1Char('/') + ti.target + QLatin1String(".sis");
+ return ti.buildDir + QLatin1Char('/') + ti.target
+ + (runSmartInstaller() ? QLatin1String("_installer") : QLatin1String(""))
+ + QLatin1String(".sis");
}
QStringList S60DeviceRunConfiguration::commandLineArguments() const
// ======== S60DeviceRunControlBase
-S60DeviceRunControlBase::S60DeviceRunControlBase(RunConfiguration *runConfiguration) :
- RunControl(runConfiguration),
+S60DeviceRunControlBase::S60DeviceRunControlBase(RunConfiguration *runConfiguration, QString mode) :
+ RunControl(runConfiguration, mode),
m_toolChain(ProjectExplorer::ToolChain::INVALID),
m_releaseDeviceAfterLauncherFinish(false),
m_handleDeviceRemoval(true),
// =============== S60DeviceRunControl
-S60DeviceRunControl::S60DeviceRunControl(ProjectExplorer::RunConfiguration *runConfiguration) :
- S60DeviceRunControlBase(runConfiguration)
+S60DeviceRunControl::S60DeviceRunControl(ProjectExplorer::RunConfiguration *runConfiguration, QString mode) :
+ S60DeviceRunControlBase(runConfiguration, mode)
{
}
// ======== S60DeviceDebugRunControl
-S60DeviceDebugRunControl::S60DeviceDebugRunControl(S60DeviceRunConfiguration *runConfiguration) :
- S60DeviceRunControlBase(runConfiguration),
+S60DeviceDebugRunControl::S60DeviceDebugRunControl(S60DeviceRunConfiguration *runConfiguration, QString mode) :
+ S60DeviceRunControlBase(runConfiguration, mode),
m_startParams(new Debugger::DebuggerStartParameters)
{
setReleaseDeviceAfterLauncherFinish(true); // Debugger controls device after install
private slots:
void proFileUpdate(Qt4ProjectManager::Internal::Qt4ProFileNode *pro);
+ void updateActiveBuildConfiguration(ProjectExplorer::BuildConfiguration *buildConfiguration);
protected:
S60DeviceRunConfiguration(ProjectExplorer::Target *parent, S60DeviceRunConfiguration *source);
ProjectExplorer::ToolChain::ToolChainType toolChainType(ProjectExplorer::BuildConfiguration *configuration) const;
void ctor();
+ bool runSmartInstaller() const;
+
QString m_proFilePath;
+ ProjectExplorer::BuildConfiguration *m_activeBuildConfiguration;
QString m_serialPortName;
QStringList m_commandLineArguments;
};
{
Q_OBJECT
public:
- explicit S60DeviceRunControlBase(ProjectExplorer::RunConfiguration *runConfiguration);
+ explicit S60DeviceRunControlBase(ProjectExplorer::RunConfiguration *runConfiguration, QString mode);
~S60DeviceRunControlBase();
virtual void start();
virtual void stop();
{
Q_OBJECT
public:
- explicit S60DeviceRunControl(ProjectExplorer::RunConfiguration *runConfiguration);
+ explicit S60DeviceRunControl(ProjectExplorer::RunConfiguration *runConfiguration, QString mode);
protected:
virtual void initLauncher(const QString &executable, trk::Launcher *);
Q_DISABLE_COPY(S60DeviceDebugRunControl)
Q_OBJECT
public:
- explicit S60DeviceDebugRunControl(S60DeviceRunConfiguration *runConfiguration);
+ explicit S60DeviceDebugRunControl(S60DeviceRunConfiguration *runConfiguration, QString mode);
virtual ~S60DeviceDebugRunControl();
virtual void stop();
// ======== S60EmulatorRunControl
-S60EmulatorRunControl::S60EmulatorRunControl(S60EmulatorRunConfiguration *runConfiguration)
- : RunControl(runConfiguration)
+S60EmulatorRunControl::S60EmulatorRunControl(S60EmulatorRunConfiguration *runConfiguration, QString mode)
+ : RunControl(runConfiguration, mode)
{
// stuff like the EPOCROOT and EPOCDEVICE env variable
Environment env = Environment::systemEnvironment();
{
Q_OBJECT
public:
- explicit S60EmulatorRunControl(S60EmulatorRunConfiguration *runConfiguration);
+ explicit S60EmulatorRunControl(S60EmulatorRunConfiguration *runConfiguration, QString mode);
~S60EmulatorRunControl() {}
void start();
void stop();
ProjectExplorer::RunControl* create(ProjectExplorer::RunConfiguration *runConfiguration, const QString &mode) {
RunConfiguration *rc = qobject_cast<RunConfiguration *>(runConfiguration);
QTC_ASSERT(rc && mode == m_mode, return 0);
- return new RunControl(rc);
+ return new RunControl(rc, mode);
}
QString displayName() const {
QString Qt4BuildConfiguration::buildDirectory() const
{
QString workingDirectory;
- if (m_shadowBuild)
- workingDirectory = m_buildDirectory;
+ if (m_shadowBuild) {
+ if (!m_buildDirectory.isEmpty())
+ workingDirectory = m_buildDirectory;
+ else
+ workingDirectory = qt4Target()->defaultBuildDirectory();
+ }
if (workingDirectory.isEmpty())
workingDirectory = target()->project()->projectDirectory();
return workingDirectory;
/// \note buildDirectory() is probably the function you want to call
QString Qt4BuildConfiguration::shadowBuildDirectory() const
{
+ if (m_buildDirectory.isEmpty())
+ return qt4Target()->defaultBuildDirectory();
return m_buildDirectory;
}
emit buildDirectoryInitialized();
}
+void Qt4BuildConfiguration::emitS60CreatesSmartInstallerChanged()
+{
+ emit s60CreatesSmartInstallerChanged();
+}
+
+
void Qt4BuildConfiguration::getConfigCommandLineArguments(QStringList *addedUserConfigs, QStringList *removedUserConfigs) const
{
QtVersion::QmakeBuildConfigs defaultBuildConfiguration = qtVersion()->defaultBuildConfig();
// used by qmake step to notify that the build directory was initialized
// not really nice
void emitBuildDirectoryInitialized();
+ // used by S60CreatePackageStep to notify that the smart installer property changed
+ // not really nice
+ void emitS60CreatesSmartInstallerChanged();
+
void getConfigCommandLineArguments(QStringList *addedUserConfigs, QStringList *removedUserConfigs) const;
// Those functions are used in a few places.
/// emitted for setQMakeBuildConfig, not emitted for qt version changes, even
/// if those change the qmakebuildconfig
void qmakeBuildConfigurationChanged();
+ /// emitted when smart installer property of S60 create package step changes
+ void s60CreatesSmartInstallerChanged();
/// emitted if the build configuration changed in a way that
/// should trigger a reevaluation of all .pro files
return false;
}
+void Qt4PriFile::rename(const QString &newName)
+{
+ // Can't happen
+ Q_ASSERT(false);
+ Q_UNUSED(newName);
+}
+
QString Qt4PriFile::fileName() const
{
return m_priFile->path();
contents.updateSubFolders(this, this);
}
-QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions() const
+QList<ProjectNode::ProjectAction> Qt4PriFileNode::supportedActions(Node *node) const
{
QList<ProjectAction> actions;
default:
break;
}
+
+ FileNode *fileNode = qobject_cast<FileNode *>(node);
+ if (fileNode && fileNode->fileType() != ProjectExplorer::ProjectFileType)
+ actions << Rename;
+
return actions;
}
if (newFilePath.isEmpty())
return false;
- if (!QFile::rename(filePath, newFilePath))
- return false;
-
QStringList dummy;
changeFiles(fileType, QStringList() << filePath, &dummy, RemoveFromProFile);
if (!dummy.isEmpty())
// newCumalativeIt and newExactIt are already incremented
}
- // If we found something to add do it
+ // If we found something to add, do it
if (!nodeToAdd.isEmpty()) {
ProFile *fileExact = includeFilesCumlative.value(nodeToAdd);
ProFile *fileCumlative = includeFilesCumlative.value(nodeToAdd);
- if (fileExact || fileCumlative) {
+
+ // Loop preventation, make sure that exact same node is not in our parent chain
+ bool loop = false;
+ ProjectExplorer::Node *n = this;
+ while ((n = n->parentFolderNode())) {
+ if (qobject_cast<Qt4PriFileNode *>(n) && n->path() == nodeToAdd) {
+ loop = true;
+ break;
+ }
+ }
+
+ if (loop) {
+ // Do nothing
+ } else if (fileExact || fileCumlative) {
Qt4PriFileNode *qt4PriFileNode = new Qt4PriFileNode(m_project, this, nodeToAdd);
+ qt4PriFileNode->setParentFolderNode(this); // Needed for loop detection
qt4PriFileNode->update(fileExact, m_readerExact, fileCumlative, m_readerCumulative);
toAdd << qt4PriFileNode;
} else {
Qt4ProFileNode *qt4ProFileNode = new Qt4ProFileNode(m_project, nodeToAdd);
+ qt4ProFileNode->setParentFolderNode(this); // Needed for loop detection
if (async)
qt4ProFileNode->asyncUpdate();
else
Qt4PriFile(Qt4PriFileNode *qt4PriFile);
virtual bool save(const QString &fileName = QString());
virtual QString fileName() const;
+ virtual void rename(const QString &newName);
virtual QString defaultPath() const;
virtual QString suggestedFileName() const;
// ProjectNode interface
- QList<ProjectAction> supportedActions() const;
+ QList<ProjectAction> supportedActions(Node *node) const;
bool hasBuildTargets() const { return false; }
return false;
}
+void Qt4ProjectFile::rename(const QString &newName)
+{
+ // Can't happen
+ Q_UNUSED(newName);
+ Q_ASSERT(false);
+}
+
QString Qt4ProjectFile::fileName() const
{
return m_filePath;
bool save(const QString &fileName = QString());
QString fileName() const;
+ virtual void rename(const QString &newName);
QString defaultPath() const;
QString suggestedFileName() const;
void Qt4Manager::buildSubDirContextMenu()
{
+ handleSubDirContexMenu(BUILD);
+}
+
+void Qt4Manager::cleanSubDirContextMenu()
+{
+ handleSubDirContexMenu(CLEAN);
+}
+
+void Qt4Manager::rebuildSubDirContextMenu()
+{
+ handleSubDirContexMenu(REBUILD);
+}
+
+void Qt4Manager::handleSubDirContexMenu(Qt4Manager::Action action)
+{
Qt4Project *qt4pro = qobject_cast<Qt4Project *>(m_contextProject);
QTC_ASSERT(qt4pro, return);
if (Qt4ProFileNode *profile = qobject_cast<Qt4ProFileNode *>(m_contextNode))
bc->setSubNodeBuild(profile);
- if (projectExplorer()->saveModifiedFiles())
- projectExplorer()->buildManager()->buildProject(bc);
+ if (projectExplorer()->saveModifiedFiles()) {
+ if (action == BUILD)
+ projectExplorer()->buildManager()->buildProject(bc);
+ else if (action == CLEAN)
+ projectExplorer()->buildManager()->cleanProject(bc);
+ else if (action == REBUILD) {
+ projectExplorer()->buildManager()->cleanProject(bc);
+ projectExplorer()->buildManager()->buildProject(bc);
+ }
+ }
bc->setSubNodeBuild(0);
}
// Return the id string of a file
static QString fileTypeId(ProjectExplorer::FileType type);
+ enum Action { BUILD, REBUILD, CLEAN };
+
public slots:
void runQMake();
void runQMakeContextMenu();
void buildSubDirContextMenu();
+ void rebuildSubDirContextMenu();
+ void cleanSubDirContextMenu();
private slots:
void editorAboutToClose(Core::IEditor *editor);
private:
QList<Qt4Project *> m_projects;
+ void handleSubDirContexMenu(Action action);
void runQMake(ProjectExplorer::Project *p, ProjectExplorer::Node *node);
Internal::Qt4ProjectManagerPlugin *m_plugin;
const char * const RUNQMAKE = "Qt4Builder.RunQMake";
const char * const RUNQMAKECONTEXTMENU = "Qt4Builder.RunQMakeContextMenu";
const char * const BUILDSUBDIR = "Qt4Builder.BuildSubDir";
+const char * const REBUILDSUBDIR = "Qt4Builder.RebuildSubDir";
+const char * const CLEANSUBDIR = "Qt4Builder.CleanSubDir";
//configurations
const char * const CONFIG_DEBUG = "debug";
m_runQMakeActionContextMenu = new QAction(qmakeIcon, tr("Run qmake"), this);
command = am->registerAction(m_runQMakeActionContextMenu, Constants::RUNQMAKECONTEXTMENU, context);
command->setAttribute(Core::Command::CA_Hide);
- command->setAttribute(Core::Command::CA_UpdateText);
mproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
msubproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
connect(m_runQMakeActionContextMenu, SIGNAL(triggered()), m_qt4ProjectManager, SLOT(runQMakeContextMenu()));
m_buildSubProjectContextMenu = new QAction(buildIcon, tr("Build"), this);
command = am->registerAction(m_buildSubProjectContextMenu, Constants::BUILDSUBDIR, context);
command->setAttribute(Core::Command::CA_Hide);
- command->setAttribute(Core::Command::CA_UpdateText);
msubproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
connect(m_buildSubProjectContextMenu, SIGNAL(triggered()), m_qt4ProjectManager, SLOT(buildSubDirContextMenu()));
+ QIcon rebuildIcon(ProjectExplorer::Constants::ICON_REBUILD);
+ rebuildIcon.addFile(ProjectExplorer::Constants::ICON_REBUILD_SMALL);
+ m_rebuildSubProjectContextMenu = new QAction(rebuildIcon, tr("Rebuild"), this);
+ command = am->registerAction(m_rebuildSubProjectContextMenu, Constants::REBUILDSUBDIR, context);
+ command->setAttribute(Core::Command::CA_Hide);
+ msubproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
+ connect(m_rebuildSubProjectContextMenu, SIGNAL(triggered()), m_qt4ProjectManager, SLOT(rebuildSubDirContextMenu()));
+
+ QIcon cleanIcon(ProjectExplorer::Constants::ICON_CLEAN);
+ cleanIcon.addFile(ProjectExplorer::Constants::ICON_CLEAN_SMALL);
+ m_cleanSubProjectContextMenu = new QAction(cleanIcon, tr("Clean"), this);
+ command = am->registerAction(m_cleanSubProjectContextMenu, Constants::CLEANSUBDIR, context);
+ command->setAttribute(Core::Command::CA_Hide);
+ msubproject->addAction(command, ProjectExplorer::Constants::G_PROJECT_BUILD);
+ connect(m_cleanSubProjectContextMenu, SIGNAL(triggered()), m_qt4ProjectManager, SLOT(cleanSubDirContextMenu()));
+
connect(m_projectExplorer,
SIGNAL(aboutToShowContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*)),
this, SLOT(updateContextMenu(ProjectExplorer::Project*, ProjectExplorer::Node*)));
m_qt4ProjectManager->setContextNode(node);
m_runQMakeActionContextMenu->setEnabled(false);
m_buildSubProjectContextMenu->setEnabled(false);
+ m_rebuildSubProjectContextMenu->setEnabled(false);
+ m_cleanSubProjectContextMenu->setEnabled(false);
Qt4ProFileNode *proFileNode = qobject_cast<Qt4ProFileNode *>(node);
if (qobject_cast<Qt4Project *>(project) && proFileNode) {
m_runQMakeActionContextMenu->setVisible(true);
m_buildSubProjectContextMenu->setVisible(true);
-
- const QString nativeBuildDir = QDir::toNativeSeparators(proFileNode->buildDir());
- m_runQMakeActionContextMenu->setText(tr("Run qmake in %1").arg(nativeBuildDir));
- m_buildSubProjectContextMenu->setText(tr("Build in %1").arg(nativeBuildDir));
+ m_rebuildSubProjectContextMenu->setVisible(true);
+ m_cleanSubProjectContextMenu->setVisible(true);
if (!m_projectExplorer->buildManager()->isBuilding(project)) {
m_runQMakeActionContextMenu->setEnabled(true);
m_buildSubProjectContextMenu->setEnabled(true);
+ m_rebuildSubProjectContextMenu->setEnabled(true);
+ m_cleanSubProjectContextMenu->setEnabled(true);
}
} else {
m_runQMakeActionContextMenu->setVisible(false);
m_buildSubProjectContextMenu->setVisible(false);
+ m_rebuildSubProjectContextMenu->setVisible(false);
+ m_cleanSubProjectContextMenu->setVisible(false);
}
}
QAction *m_runQMakeAction;
QAction *m_runQMakeActionContextMenu;
QAction *m_buildSubProjectContextMenu;
+ QAction *m_rebuildSubProjectContextMenu;
+ QAction *m_cleanSubProjectContextMenu;
GettingStartedWelcomePage *m_welcomePage;
};
-<plugin name="RegExp" version="1.3.84" compatVersion="1.3.84">
+<plugin name="RegExp" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Regular Expression test widget.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
-<plugin name="ResourceEditor" version="1.3.84" compatVersion="1.3.84">
+<plugin name="ResourceEditor" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Editor for qrc files.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
return true;
}
+void ResourceEditorFile::rename(const QString &newName)
+{
+ m_parent->m_resourceEditor->setFileName(newName);
+ emit changed();
+}
+
QString ResourceEditorW::id() const {
return QLatin1String(ResourceEditor::Constants::RESOURCEEDITOR_ID);
}
QString defaultPath() const;
QString suggestedFileName() const;
virtual QString mimeType() const;
+ virtual void rename(const QString &newName);
private:
const QString m_mimeType;
-<plugin name="Snippets" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Snippets" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Code snippet plugin.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
</dependencyList>
</plugin>
-<plugin name="Subversion" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Subversion" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Subversion integration.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="VCSBase" version="1.3.84"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="VCSBase" version="2.0.80"/>
</dependencyList>
</plugin>
switch (operation) {
case AddOperation:
case DeleteOperation:
+ case MoveOperation:
case AnnotateOperation:
break;
case OpenOperation:
return m_plugin->vcsDelete(fi.absolutePath(), fi.fileName());
}
+bool SubversionControl::vcsMove(const QString &from, const QString &to)
+{
+ const QFileInfo fromInfo(from);
+ const QFileInfo toInfo(to);
+ return m_plugin->vcsMove(fromInfo.absolutePath(), fromInfo.absoluteFilePath(), toInfo.absoluteFilePath());
+}
+
bool SubversionControl::vcsCreateRepository(const QString &)
{
return false;
return false;
}
-bool SubversionControl::managesDirectory(const QString &directory) const
-{
- return m_plugin->managesDirectory(directory);
-}
-
-QString SubversionControl::findTopLevelForDirectory(const QString &directory) const
+bool SubversionControl::managesDirectory(const QString &directory, QString *topLevel) const
{
- return m_plugin->findTopLevelForDirectory(directory);
+ return m_plugin->managesDirectory(directory, topLevel);
}
bool SubversionControl::vcsAnnotate(const QString &file, int line)
explicit SubversionControl(SubversionPlugin *plugin);
virtual QString displayName() const;
- virtual bool managesDirectory(const QString &directory) const;
- virtual QString findTopLevelForDirectory(const QString &directory) const;
+ virtual bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
virtual bool supportsOperation(Operation operation) const;
virtual bool vcsOpen(const QString &fileName);
virtual bool vcsAdd(const QString &fileName);
virtual bool vcsDelete(const QString &filename);
+ virtual bool vcsMove(const QString &from, const QString &to);
virtual bool vcsCreateRepository(const QString &directory);
virtual QString vcsCreateSnapshot(const QString &topLevel);
// To describe a complete change, find the top level and then do
//svn diff -r 472958:472959 <top level>
const QFileInfo fi(source);
- const QString topLevel = findTopLevelForDirectory(fi.isDir() ? source : fi.absolutePath());
- if (topLevel.isEmpty())
+ QString topLevel;
+ const bool manages = managesDirectory(fi.isDir() ? source : fi.absolutePath(), &topLevel);
+ if (!manages || topLevel.isEmpty())
return;
if (Subversion::Constants::debug)
qDebug() << Q_FUNC_INFO << source << topLevel << changeNr;
if (!path.isEmpty())
path += slash;
path += relativePath.at(p);
- if (!managesDirectory(QDir(path))) {
+ if (!checkSVNSubDir(QDir(path))) {
QStringList addDirArgs;
addDirArgs << QLatin1String("add") << QLatin1String("--non-recursive") << QDir::toNativeSeparators(path);
const SubversionResponse addDirResponse = runSvn(workingDir, addDirArgs, m_settings.timeOutMS(), true);
return !response.error;
}
+bool SubversionPlugin::vcsMove(const QString &workingDir, const QString &from, const QString &to)
+{
+ QStringList args(QLatin1String("move"));
+ args << QDir::toNativeSeparators(from) << QDir::toNativeSeparators(to);
+ qDebug()<<args;
+ const SubversionResponse response = runSvn(workingDir, args, m_settings.timeOutMS(), true);
+ qDebug() << response.stdOut << "\n"<<response.stdErr;
+ return !response.error;
+}
+
/* Subversion has ".svn" directory in each directory
* it manages. The top level is the first directory
* under the directory that does not have a ".svn". */
-bool SubversionPlugin::managesDirectory(const QString &directory) const
+bool SubversionPlugin::managesDirectory(const QString &directory, QString *topLevel /* = 0 */) const
{
const QDir dir(directory);
- const bool rc = dir.exists() && managesDirectory(dir);
- if (Subversion::Constants::debug)
- qDebug() << "SubversionPlugin::managesDirectory" << directory << rc;
- return rc;
+ if (topLevel)
+ topLevel->clear();
+ bool manages = false;
+ do {
+ if (!dir.exists() || !checkSVNSubDir(dir))
+ break;
+ manages = true;
+ if (!topLevel)
+ break;
+ /* Recursing up, the top level is a child of the first directory that does
+ * not have a ".svn" directory. The starting directory must be a managed
+ * one. Go up and try to find the first unmanaged parent dir. */
+ QDir lastDirectory = dir;
+ for (QDir parentDir = lastDirectory; parentDir.cdUp() ; lastDirectory = parentDir) {
+ if (!checkSVNSubDir(parentDir)) {
+ *topLevel = lastDirectory.absolutePath();
+ break;
+ }
+ }
+ } while (false);
+ if (Subversion::Constants::debug) {
+ QDebug nsp = qDebug().nospace();
+ nsp << "SubversionPlugin::managesDirectory" << directory << manages;
+ if (topLevel)
+ nsp << *topLevel;
+ }
+ return manages;
}
-bool SubversionPlugin::managesDirectory(const QDir &directory) const
+// Check whether SVN management subdirs exist.
+bool SubversionPlugin::checkSVNSubDir(const QDir &directory) const
{
const int dirCount = m_svnDirectories.size();
for (int i = 0; i < dirCount; i++) {
return false;
}
-QString SubversionPlugin::findTopLevelForDirectory(const QString &directory) const
-{
- // Debug wrapper
- const QString rc = findTopLevelForDirectoryI(directory);
- if (Subversion::Constants::debug)
- qDebug() << "SubversionPlugin::findTopLevelForDirectory" << directory << rc;
- return rc;
-}
-
-QString SubversionPlugin::findTopLevelForDirectoryI(const QString &directory) const
-{
- /* Recursing up, the top level is a child of the first directory that does
- * not have a ".svn" directory. The starting directory must be a managed
- * one. Go up and try to find the first unmanaged parent dir. */
- QDir lastDirectory = QDir(directory);
- if (!lastDirectory.exists() || !managesDirectory(lastDirectory))
- return QString();
- for (QDir parentDir = lastDirectory; parentDir.cdUp() ; lastDirectory = parentDir) {
- if (!managesDirectory(parentDir))
- return lastDirectory.absolutePath();
- }
- return QString();
-}
-
SubversionControl *SubversionPlugin::subVersionControl() const
{
return static_cast<SubversionControl *>(versionControl());
bool vcsAdd14(const QString &workingDir, const QString &fileName);
bool vcsAdd15(const QString &workingDir, const QString &fileName);
bool vcsDelete(const QString &workingDir, const QString &fileName);
- bool managesDirectory(const QString &directory) const;
- QString findTopLevelForDirectory(const QString &directory) const;
+ bool vcsMove(const QString &workingDir, const QString &from, const QString &to);
+ bool managesDirectory(const QString &directory, QString *topLevel = 0) const;
static SubversionPlugin *subversionPluginInstance();
bool enableAnnotationContextMenu = false);
void svnStatus(const QString &workingDir, const QStringList &relativePath = QStringList());
void svnUpdate(const QString &workingDir, const QStringList &relativePaths = QStringList());
- bool managesDirectory(const QDir &directory) const;
- QString findTopLevelForDirectoryI(const QString &directory) const;
+ bool checkSVNSubDir(const QDir &directory) const;
void startCommit(const QString &workingDir, const QStringList &files = QStringList());
bool commit(const QString &messageFile, const QStringList &subVersionFileList);
void cleanCommitMessageFile();
-<plugin name="TextEditor" version="1.3.84" compatVersion="1.3.84">
+<plugin name="TextEditor" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Text editor framework and the implementation of the basic text editor.</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="Find" version="1.3.84"/>
- <dependency name="Locator" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="Find" version="2.0.80"/>
+ <dependency name="Locator" version="2.0.80"/>
</dependencyList>
</plugin>
return true;
}
+void BaseTextDocument::rename(const QString &newName)
+{
+ const QFileInfo fi(newName);
+ m_fileName = QDir::cleanPath(fi.absoluteFilePath());
+ emit titleChanged(fi.fileName());
+ emit changed();
+}
+
bool BaseTextDocument::isReadOnly() const
{
if (m_isBinaryData || m_hasDecodingError)
void reload(ReloadFlag flag, ChangeType type);
virtual QString mimeType() const;
void setMimeType(const QString &mt);
+ virtual void rename(const QString &newName);
virtual QString defaultPath() const { return m_defaultPath; }
virtual QString suggestedFileName() const { return m_suggestedFileName; }
using namespace TextEditor;
-bool Parenthesis::hasClosingCollapse(const Parentheses &parentheses)
-{
- return closeCollapseAtPos(parentheses) >= 0;
-}
-
-int Parenthesis::closeCollapseAtPos(const Parentheses &parentheses)
-{
- int depth = 0;
- for (int i = 0; i < parentheses.size(); ++i) {
- const Parenthesis &p = parentheses.at(i);
- if (p.chr == QLatin1Char('{')
- || p.chr == QLatin1Char('+')
- || p.chr == QLatin1Char('[')) {
- ++depth;
- } else if (p.chr == QLatin1Char('}')
- || p.chr == QLatin1Char('-')
- || p.chr == QLatin1Char(']')) {
- if (--depth < 0)
- return p.pos;
- }
- }
- return -1;
-}
-
-int Parenthesis::collapseAtPos(const Parentheses &parentheses, QChar *character)
-{
- int result = -1;
- QChar c;
-
- int depth = 0;
- for (int i = 0; i < parentheses.size(); ++i) {
- const Parenthesis &p = parentheses.at(i);
- if (p.chr == QLatin1Char('{')
- || p.chr == QLatin1Char('+')
- || p.chr == QLatin1Char('[')) {
- if (depth == 0) {
- result = p.pos;
- c = p.chr;
- }
- ++depth;
- } else if (p.chr == QLatin1Char('}')
- || p.chr == QLatin1Char('-')
- || p.chr == QLatin1Char(']')) {
- if (--depth < 0)
- depth = 0;
- result = -1;
- }
- }
- if (result >= 0 && character)
- *character = c;
- return result;
-}
-
-
TextBlockUserData::~TextBlockUserData()
{
TextMarks marks = m_marks;
}
}
-int TextBlockUserData::collapseAtPos(QChar *character) const
-{
- return Parenthesis::collapseAtPos(m_parentheses, character);
-}
-
int TextBlockUserData::braceDepthDelta() const
{
int delta = 0;
return delta;
}
-
-QTextBlock TextBlockUserData::testCollapse(const QTextBlock& block)
-{
- QTextBlock info = block;
- if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter)
- ;
- else if (block.next().userData()
- && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
- == TextBlockUserData::CollapseThis)
- info = block.next();
- else
- return QTextBlock();
- int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos();
- if (pos < 0)
- return QTextBlock();
- QTextCursor cursor(info);
- cursor.setPosition(cursor.position() + pos);
- matchCursorForward(&cursor);
- return cursor.block();
-}
-
-void TextBlockUserData::doCollapse(const QTextBlock& block, bool visible)
-{
- QTextBlock info = block;
- if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter)
- ;
- else if (block.next().userData()
- && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
- == TextBlockUserData::CollapseThis)
- info = block.next();
- else {
- if (visible && !block.next().isVisible()) {
- // no match, at least unfold!
- QTextBlock b = block.next();
- while (b.isValid() && !b.isVisible()) {
- b.setVisible(true);
- b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
- b = b.next();
- }
- }
- return;
- }
- int pos = static_cast<TextBlockUserData*>(info.userData())->collapseAtPos();
- if (pos < 0)
- return;
- QTextCursor cursor(info);
- cursor.setPosition(cursor.position() + pos);
- if (matchCursorForward(&cursor) != Match) {
- if (visible) {
- // no match, at least unfold!
- QTextBlock b = block.next();
- while (b.isValid() && !b.isVisible()) {
- b.setVisible(true);
- b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
- b = b.next();
- }
- }
- return;
- }
-
- QTextBlock b = block.next();
- while (b < cursor.block()) {
- b.setVisible(visible);
- b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
- if (visible) {
- TextBlockUserData *data = canCollapse(b);
- if (data && data->collapsed()) {
- QTextBlock end = testCollapse(b);
- if (data->collapseIncludesClosure())
- end = end.next();
- if (end.isValid()) {
- b = end;
- continue;
- }
- }
- }
- b = b.next();
- }
-
- bool collapseIncludesClosure = hasClosingCollapseAtEnd(b);
- if (collapseIncludesClosure) {
- b.setVisible(visible);
- b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0);
- }
- static_cast<TextBlockUserData*>(info.userData())->setCollapseIncludesClosure(collapseIncludesClosure);
- static_cast<TextBlockUserData*>(info.userData())->setCollapsed(!block.next().isVisible());
-
-}
-
-
TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c)
{
QTextBlock block = cursor->block();
if (delta)
setBraceDepth(block, braceDepth(block) + delta);
}
+
+void BaseTextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent)
+{
+ if (indent == 0) {
+ if (TextBlockUserData *userData = testUserData(block))
+ userData->setFoldingIndent(0);
+ } else {
+ userData(block)->setFoldingIndent(qMax(0,indent));
+ }
+}
+
+int BaseTextDocumentLayout::foldingIndent(const QTextBlock &block)
+{
+ if (TextBlockUserData *userData = testUserData(block))
+ return userData->foldingIndent();
+ return 0;
+}
+
+void BaseTextDocumentLayout::changeFoldingIndent(QTextBlock &block, int delta)
+{
+ if (delta)
+ setFoldingIndent(block, foldingIndent(block) + delta);
+}
+
+bool BaseTextDocumentLayout::canFold(const QTextBlock &block)
+{
+ return (block.next().isValid() && foldingIndent(block.next()) > foldingIndent(block));
+}
+
+bool BaseTextDocumentLayout::isFolded(const QTextBlock &block)
+{
+ if (TextBlockUserData *userData = testUserData(block))
+ return userData->folded();
+ return false;
+}
+
+void BaseTextDocumentLayout::setFolded(const QTextBlock &block, bool folded)
+{
+ if (folded)
+ userData(block)->setFolded(true);
+ else {
+ if (TextBlockUserData *userData = testUserData(block))
+ return userData->setFolded(false);
+ }
+}
+
+void BaseTextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold)
+{
+ if (!canFold(block))
+ return;
+ QTextBlock b = block.next();
+
+ int indent = foldingIndent(block);
+ while (b.isValid() && foldingIndent(b) > indent && b.next().isValid()) {
+ b.setVisible(unfold);
+ b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0);
+ if (unfold) { // do not unfold folded sub-blocks
+ if (isFolded(b) && b.next().isValid()) {
+ int jndent = foldingIndent(b);
+ b = b.next();
+ while (b.isValid() && foldingIndent(b) > jndent)
+ b = b.next();
+ continue;
+ }
+ }
+ b = b.next();
+ }
+ setFolded(block, !unfold);
+}
+
+
Type type;
QChar chr;
int pos;
- static int collapseAtPos(const Parentheses &parentheses, QChar *character = 0);
- static int closeCollapseAtPos(const Parentheses &parentheses);
- static bool hasClosingCollapse(const Parentheses &parentheses);
};
{
public:
- enum CollapseMode { NoCollapse , CollapseThis, CollapseAfter };
- enum ClosingCollapseMode { NoClosingCollapse, ClosingCollapse, ClosingCollapseAtEnd };
-
inline TextBlockUserData()
- : m_collapseIncludesClosure(false),
- m_collapseMode(NoCollapse),
- m_closingCollapseMode(NoClosingCollapse),
- m_collapsed(false),
- m_ifdefedOut(false) {}
+ : m_folded(false),
+ m_ifdefedOut(false),
+ m_foldingIndent(0),
+ m_foldingStartIncluded(false),
+ m_foldingEndIncluded(false){}
~TextBlockUserData();
inline TextMarks marks() const { return m_marks; }
inline void clearMarks() { m_marks.clear(); }
inline void documentClosing() { Q_FOREACH(ITextMark *tm, m_marks) { tm->documentClosing(); } m_marks.clear();}
- inline CollapseMode collapseMode() const { return (CollapseMode)m_collapseMode; }
- inline void setCollapseMode(CollapseMode c) { m_collapseMode = c; }
-
- inline void setClosingCollapseMode(ClosingCollapseMode c) { m_closingCollapseMode = c; }
- inline ClosingCollapseMode closingCollapseMode() const { return (ClosingCollapseMode) m_closingCollapseMode; }
-
- inline bool hasClosingCollapse() const { return closingCollapseMode() != NoClosingCollapse; }
- inline bool hasClosingCollapseAtEnd() const { return closingCollapseMode() == ClosingCollapseAtEnd; }
- inline bool hasClosingCollapseInside() const { return closingCollapseMode() == ClosingCollapse; }
-
- inline void setCollapsed(bool b) { m_collapsed = b; }
- inline bool collapsed() const { return m_collapsed; }
-
- inline void setCollapseIncludesClosure(bool b) { m_collapseIncludesClosure = b; }
- inline bool collapseIncludesClosure() const { return m_collapseIncludesClosure; }
+ inline void setFolded(bool b) { m_folded = b; }
+ inline bool folded() const { return m_folded; }
inline void setParentheses(const Parentheses &parentheses) { m_parentheses = parentheses; }
inline void clearParentheses() { m_parentheses.clear(); }
inline bool clearIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = false; return result;}
inline bool ifdefedOut() const { return m_ifdefedOut; }
- inline static TextBlockUserData *canCollapse(const QTextBlock &block) {
- TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData());
- if (!data || data->collapseMode() != CollapseAfter) {
- data = static_cast<TextBlockUserData*>(block.next().userData());
- if (!data || data->collapseMode() != TextBlockUserData::CollapseThis)
- data = 0;
- }
- if (data && data->m_ifdefedOut)
- data = 0;
- return data;
- }
-
- inline static bool hasCollapseAfter(const QTextBlock & block)
- {
- if (!block.isValid()) {
- return false;
- } else if (block.next().isValid()) {
- TextBlockUserData *data = static_cast<TextBlockUserData*>(block.next().userData());
- if (data && data->collapseMode() == TextBlockUserData::CollapseThis && !data->m_ifdefedOut)
- return true;
- }
- return false;
- }
-
- inline static bool hasClosingCollapse(const QTextBlock &block) {
- TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData());
- return (data && data->hasClosingCollapse());
- }
-
- inline static bool hasClosingCollapseAtEnd(const QTextBlock &block) {
- TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData());
- return (data && data->hasClosingCollapseAtEnd());
- }
-
- inline static bool hasClosingCollapseInside(const QTextBlock &block) {
- TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData());
- return (data && data->hasClosingCollapseInside());
- }
-
- static QTextBlock testCollapse(const QTextBlock& block);
- static void doCollapse(const QTextBlock& block, bool visible);
-
- int collapseAtPos(QChar *character = 0) const;
enum MatchType { NoMatch, Match, Mismatch };
static MatchType checkOpenParenthesis(QTextCursor *cursor, QChar c);
static bool findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition = false);
static bool findNextBlockClosingParenthesis(QTextCursor *cursor);
+ int foldingIndent() const { return m_foldingIndent; }
+ void setFoldingIndent(int indent) { m_foldingIndent = indent; }
+ void setFoldingStartIncluded(bool included) { m_foldingStartIncluded = included; }
+ bool foldingStartIncluded() const { return m_foldingStartIncluded; }
+ void setFoldingEndIncluded(bool included) { m_foldingEndIncluded = included; }
+ bool foldingEndIncluded() const { return m_foldingEndIncluded; }
+
private:
TextMarks m_marks;
- uint m_collapseIncludesClosure : 1;
- uint m_collapseMode : 4;
- uint m_closingCollapseMode : 4;
- uint m_collapsed : 1;
+ uint m_folded : 1;
uint m_ifdefedOut : 1;
+ uint m_foldingIndent : 16;
+ uint m_foldingStartIncluded : 1;
+ uint m_foldingEndIncluded : 1;
Parentheses m_parentheses;
};
static int braceDepth(const QTextBlock &block);
static void setBraceDepth(QTextBlock &block, int depth);
static void changeBraceDepth(QTextBlock &block, int delta);
+ static void setFoldingIndent(const QTextBlock &block, int indent);
+ static int foldingIndent(const QTextBlock &block);
+ static void changeFoldingIndent(QTextBlock &block, int delta);
+ static bool canFold(const QTextBlock &block);
+ static void doFoldOrUnfold(const QTextBlock& block, bool unfold);
+ static bool isFolded(const QTextBlock &block);
+ static void setFolded(const QTextBlock &block, bool folded);
static TextBlockUserData *testUserData(const QTextBlock &block) {
return static_cast<TextBlockUserData*>(block.userData());
viewport()->setMouseTracking(true);
d->extraAreaSelectionAnchorBlockNumber
= d->extraAreaToggleMarkBlockNumber
- = d->extraAreaHighlightCollapseBlockNumber
- = d->extraAreaHighlightCollapseColumn
+ = d->extraAreaHighlightFoldedBlockNumber
= -1;
- d->visibleCollapsedBlockNumber = d->suggestedVisibleCollapsedBlockNumber = -1;
+ d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber = -1;
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(slotUpdateExtraAreaWidth()));
connect(this, SIGNAL(modificationChanged(bool)), this, SLOT(slotModificationChanged(bool)));
delete dlg;
}
-static int collapseBoxWidth(const QFontMetrics &fm)
+static int foldBoxWidth(const QFontMetrics &fm)
{
const int lineSpacing = fm.lineSpacing();
return lineSpacing + lineSpacing%2 + 1;
/*
Collapses the first comment in a file, if there is only whitespace above
*/
-void BaseTextEditorPrivate::collapseLicenseHeader()
+void BaseTextEditorPrivate::foldLicenseHeader()
{
QTextDocument *doc = q->document();
BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
QTextBlock block = doc->firstBlock();
const TabSettings &ts = m_document->tabSettings();
while (block.isValid() && block.isVisible()) {
- TextBlockUserData *data = TextBlockUserData::canCollapse(block);
- if (data && block.next().isVisible()) {
- QChar character;
- static_cast<TextBlockUserData*>(data)->collapseAtPos(&character);
- if (character != QLatin1Char('+'))
- break; // not a comment
- TextBlockUserData::doCollapse(block, false);
- moveCursorVisible();
- documentLayout->requestUpdate();
- documentLayout->emitDocumentSizeChanged();
- break;
- }
QString text = block.text();
+ if (BaseTextDocumentLayout::canFold(block) && block.next().isVisible()) {
+ if (text.trimmed().startsWith(QLatin1String("/*"))) {
+ BaseTextDocumentLayout::doFoldOrUnfold(block, false);
+ moveCursorVisible();
+ documentLayout->requestUpdate();
+ documentLayout->emitDocumentSizeChanged();
+ break;
+ }
+ }
if (ts.firstNonSpace(text) < text.size())
break;
block = block.next();
setTextCursor(cursor);
}
+void BaseTextEditor::insertLineAbove()
+{
+ QTextCursor cursor = textCursor();
+ cursor.beginEditBlock();
+ cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
+ cursor.insertBlock();
+ indent(document(), cursor, QChar::Null);
+ cursor.endEditBlock();
+ setTextCursor(cursor);
+}
+
+void BaseTextEditor::insertLineBelow()
+{
+ QTextCursor cursor = textCursor();
+ cursor.beginEditBlock();
+ cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
+ cursor.insertBlock();
+ indent(document(), cursor, QChar::Null);
+ cursor.endEditBlock();
+ setTextCursor(cursor);
+}
+
void BaseTextEditor::moveLineUp()
{
moveLineUpDown(true);
QTextCursor cursor = textCursor();
QTextCursor move = cursor;
- move.setVisualNavigation(false); // this opens collapsed items instead of destroying them
+ move.setVisualNavigation(false); // this opens folded items instead of destroying them
if (d->m_moveLineUndoHack)
move.joinPreviousEditBlock();
QToolTip::hideText();
d->m_moveLineUndoHack = false;
- d->clearVisibleCollapsedBlock();
+ d->clearVisibleFoldedBlock();
QKeyEvent *original_e = e;
d->m_lastEventWasBlockSelectionEvent = false;
stream << column;
// store code folding state
- QList<int> collapsedBlocks;
+ QList<int> foldedBlocks;
QTextBlock block = document()->firstBlock();
while (block.isValid()) {
- if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapsed()) {
+ if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->folded()) {
int number = block.blockNumber();
- if (static_cast<TextBlockUserData*>(block.userData())->collapseMode()
- == TextBlockUserData::CollapseThis)
- number--;
- collapsedBlocks += number;
+ foldedBlocks += number;
}
block = block.next();
}
- stream << collapsedBlocks;
+ stream << foldedBlocks;
return state;
}
{
if (state.isEmpty()) {
if (d->m_displaySettings.m_autoFoldFirstComment)
- d->collapseLicenseHeader();
+ d->foldLicenseHeader();
return false;
}
int version;
foreach(int blockNumber, collapsedBlocks) {
QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber));
if (block.isValid())
- TextBlockUserData::doCollapse(block, false);
+ BaseTextDocumentLayout::doFoldOrUnfold(block, false);
}
} else {
if (d->m_displaySettings.m_autoFoldFirstComment)
- d->collapseLicenseHeader();
+ d->foldLicenseHeader();
}
d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
m_parenthesesMatchingEnabled(false),
m_autoParenthesesEnabled(true),
m_extraArea(0),
- m_mouseOnCollapsedMarker(false),
+ m_mouseOnFoldedMarker(false),
m_marksVisible(false),
m_codeFoldingVisible(false),
m_codeFoldingSupported(false),
QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height())));
}
-QRect BaseTextEditor::collapseBox()
+QRect BaseTextEditor::foldBox()
{
- if (d->m_highlightBlocksInfo.isEmpty() || d->extraAreaHighlightCollapseBlockNumber < 0)
+ if (d->m_highlightBlocksInfo.isEmpty() || d->extraAreaHighlightFoldedBlockNumber < 0)
return QRect();
QTextBlock begin = document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last());
- if (TextBlockUserData::hasCollapseAfter(begin.previous()))
- begin = begin.previous();
-
QTextBlock end = document()->findBlockByNumber(d->m_highlightBlocksInfo.close.first());
if (!begin.isValid() || !end.isValid())
return QRect();
QRectF br = blockBoundingGeometry(begin).translated(contentOffset());
QRectF er = blockBoundingGeometry(end).translated(contentOffset());
- return QRect(d->m_extraArea->width() - collapseBoxWidth(fontMetrics()),
+ return QRect(d->m_extraArea->width() - foldBoxWidth(fontMetrics()),
int(br.top()),
- collapseBoxWidth(fontMetrics()),
+ foldBoxWidth(fontMetrics()),
er.bottom() - br.top());
}
-QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const
+QTextBlock BaseTextEditor::foldedBlockAt(const QPoint &pos, QRect *box) const
{
QPointF offset(contentOffset());
QTextBlock block = firstVisibleBlock();
for (int i = 0; i <= depth; ++i) {
int vi = i > 0 ? d->m_highlightBlocksInfo.visualIndent.at(i-1) : 0;
painter.fillRect(rr.adjusted(vi, 0, -8*i, 0), calcBlendColor(baseColor, i, count));
- if (d->m_highlightCurrentLine && blockFP == textCursorBlock) {
- QRectF rrr = blockFP.layout()->lineForTextPosition(textCursor().positionInBlock()).rect();
- rrr.moveTop(rrr.top() + rr.top());
- rrr.setLeft(rr.left());
- rrr.setRight(rr.right());
- painter.fillRect(rrr.adjusted(vi, 0, -8*i, 0),
- calcBlendColor(d->m_currentLineFormat.background().color(), i, count));
- }
}
}
rr.moveTop(rr.top() + r.top());
rr.setLeft(0);
rr.setRight(viewportRect.width() - offset.x());
- if (lineX > 0) {
- if (lineX < rr.right()) {
- QRectF rrr = rr;
- rrr.setLeft(lineX);
- painter.fillRect(rrr,
- blendColors(
- d->m_currentLineFormat.background().color(),
- d->m_ifdefedOutFormat.background().color(),
- 50)
- );
- const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white;
- const QPen pen = painter.pen();
- painter.setPen(blendColors(d->m_currentLineFormat.background().color(), col, 32));
- painter.drawLine(QPointF(lineX, rr.top()), QPointF(lineX, rr.bottom()));
- painter.setPen(pen);
- }
- rr.setRight(qMin(lineX, rr.right()));
- }
- if (d->m_highlightBlocksInfo.isEmpty() || BaseTextDocumentLayout::ifdefedOut(block))
- painter.fillRect(rr, d->m_currentLineFormat.background());
+ QColor color = d->m_currentLineFormat.background().color();
+ // set alpha, otherwise we cannot see block highlighting and find scope underneath
+ color.setAlpha(128);
+ painter.fillRect(rr, color);
}
bool drawCursor = ((editable || true) // we want the cursor in read-only mode
block = block.next();
if (!block.isVisible()) {
- if (block.blockNumber() == d->visibleCollapsedBlockNumber) {
+ if (block.blockNumber() == d->visibleFoldedBlockNumber) {
visibleCollapsedBlock = block;
visibleCollapsedBlockOffset = offset + QPointF(0,1);
}
QString replacement = QLatin1String("...");
- QTextBlock info = block;
- if (block.userData()
- && static_cast<TextBlockUserData*>(block.userData())->collapseMode() == TextBlockUserData::CollapseAfter)
- ;
- else if (block.next().userData()
- && static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
- == TextBlockUserData::CollapseThis) {
- replacement.prepend(nextBlock.text().trimmed().left(1));
- info = nextBlock;
+ if (TextBlockUserData *nextBlockUserData = BaseTextDocumentLayout::testUserData(nextBlock)) {
+ if (nextBlockUserData->foldingStartIncluded())
+ replacement.prepend(nextBlock.text().trimmed().left(1));
}
-
block = nextVisibleBlock.previous();
if (!block.isValid())
block = doc->lastBlock();
- if (info.userData()
- && static_cast<TextBlockUserData*>(info.userData())->collapseIncludesClosure()) {
- QString right = block.text().trimmed();
- if (right.endsWith(QLatin1Char(';'))) {
- right.chop(1);
- right = right.trimmed();
- replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
- replacement.append(QLatin1Char(';'));
- } else {
- replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
+ if (TextBlockUserData *blockUserData = BaseTextDocumentLayout::testUserData(block)) {
+ if (blockUserData->foldingEndIncluded()) {
+ QString right = block.text().trimmed();
+ if (right.endsWith(QLatin1Char(';'))) {
+ right.chop(1);
+ right = right.trimmed();
+ replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
+ replacement.append(QLatin1Char(';'));
+ } else {
+ replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1));
+ }
}
}
+
if (selectThis)
painter.setPen(palette().highlightedText().color());
painter.drawText(collapseRect, Qt::AlignCenter, replacement);
space += 4;
if (d->m_codeFoldingVisible)
- space += collapseBoxWidth(fm);
+ space += foldBoxWidth(fm);
return space;
}
if (d->m_marksVisible)
markWidth += fm.lineSpacing();
- const int collapseColumnWidth = d->m_codeFoldingVisible ? collapseBoxWidth(fm): 0;
+ const int collapseColumnWidth = d->m_codeFoldingVisible ? foldBoxWidth(fm): 0;
const int extraAreaWidth = d->m_extraArea->width() - collapseColumnWidth;
painter.fillRect(e->rect(), pal.color(QPalette::Base));
if (d->m_codeFoldingVisible) {
- bool collapseThis = false;
- bool collapseAfter = false;
- bool hasClosingCollapse = false;
-
- if (TextBlockUserData *userData = static_cast<TextBlockUserData*>(block.userData())) {
- if (!userData->ifdefedOut()) {
- collapseAfter = (userData->collapseMode() == TextBlockUserData::CollapseAfter);
- collapseThis = (userData->collapseMode() == TextBlockUserData::CollapseThis);
- hasClosingCollapse = userData->hasClosingCollapse() && (previousBraceDepth > 0);
- }
- }
-
- int extraAreaHighlightCollapseBlockNumber = -1;
- int extraAreaHighlightCollapseEndBlockNumber = -1;
+ int extraAreaHighlightFoldBlockNumber = -1;
+ int extraAreaHighlightFoldEndBlockNumber = -1;
bool endIsVisible = false;
if (!d->m_highlightBlocksInfo.isEmpty()) {
- extraAreaHighlightCollapseBlockNumber = d->m_highlightBlocksInfo.open.last();
- extraAreaHighlightCollapseEndBlockNumber = d->m_highlightBlocksInfo.close.first();
- endIsVisible = doc->findBlockByNumber(extraAreaHighlightCollapseEndBlockNumber).isVisible();
-
- QTextBlock before = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber-1);
- if (TextBlockUserData::hasCollapseAfter(before)) {
- extraAreaHighlightCollapseBlockNumber--;
- }
+ extraAreaHighlightFoldBlockNumber = d->m_highlightBlocksInfo.open.last();
+ extraAreaHighlightFoldEndBlockNumber = d->m_highlightBlocksInfo.close.first();
+ endIsVisible = doc->findBlockByNumber(extraAreaHighlightFoldEndBlockNumber).isVisible();
+
+// QTextBlock before = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber-1);
+// if (TextBlockUserData::hasCollapseAfter(before)) {
+// extraAreaHighlightCollapseBlockNumber--;
+// }
}
TextBlockUserData *nextBlockUserData = BaseTextDocumentLayout::testUserData(nextBlock);
- bool collapseNext = nextBlockUserData
- && nextBlockUserData->collapseMode() == TextBlockUserData::CollapseThis
- && !nextBlockUserData->ifdefedOut();
+ bool drawBox = nextBlockUserData
+ && BaseTextDocumentLayout::foldingIndent(block) < nextBlockUserData->foldingIndent();
+
- bool nextHasClosingCollapse = nextBlockUserData
- && nextBlockUserData->hasClosingCollapseInside()
- && nextBlockUserData->ifdefedOut();
- bool drawBox = ((collapseAfter || collapseNext) && !nextHasClosingCollapse);
- bool active = blockNumber == extraAreaHighlightCollapseBlockNumber;
- bool drawStart = drawBox && active;
- bool drawEnd = blockNumber == extraAreaHighlightCollapseEndBlockNumber || (drawStart && !endIsVisible);
- bool hovered = blockNumber >= extraAreaHighlightCollapseBlockNumber
- && blockNumber <= extraAreaHighlightCollapseEndBlockNumber;
+ bool active = blockNumber == extraAreaHighlightFoldBlockNumber;
- int boxWidth = collapseBoxWidth(fm);
+ bool drawStart = active;
+ bool drawEnd = blockNumber == extraAreaHighlightFoldEndBlockNumber || (drawStart && !endIsVisible);
+ bool hovered = blockNumber >= extraAreaHighlightFoldBlockNumber
+ && blockNumber <= extraAreaHighlightFoldEndBlockNumber;
+
+ int boxWidth = foldBoxWidth(fm);
if (hovered) {
int itop = qRound(top);
int ibottom = qRound(bottom);
{
#if 0
qDebug() << "block" << textCursor().blockNumber()+1
- << "depth:" << BaseTextDocumentLayout::braceDepth(textCursor().block())
- << '/' << BaseTextDocumentLayout::braceDepth(document()->lastBlock());
+ << "brace depth:" << BaseTextDocumentLayout::braceDepth(textCursor().block())
+ << "indent:" << BaseTextDocumentLayout::userData(textCursor().block())->foldingIndent();
#endif
if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) {
Core::EditorManager::instance()->addCurrentPositionToNavigationHistory(editableInterface(), d->m_tempNavigationState);
if (d->m_displaySettings.m_highlightBlocks) {
QTextCursor cursor = textCursor();
- d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber();
- d->extraAreaHighlightCollapseColumn = cursor.position() - cursor.block().position();
+ d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
d->m_highlightBlocksTimer->start(100);
}
}
/* The syntax highlighting state changes. This opens up for
the possibility that the paragraph has braces that support
code folding. In this case, do the save thing and also
- update the previous block, which might contain a collapse
+ update the previous block, which might contain a fold
box which now is invalid.*/
emit requestBlockUpdate(block.previous());
}
int timeout = 4900 / (delta * delta);
d->autoScrollTimer.start(timeout, this);
- } else if (e->timerId() == d->collapsedBlockTimer.timerId()) {
- d->visibleCollapsedBlockNumber = d->suggestedVisibleCollapsedBlockNumber;
- d->suggestedVisibleCollapsedBlockNumber = -1;
- d->collapsedBlockTimer.stop();
+ } else if (e->timerId() == d->foldedBlockTimer.timerId()) {
+ d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber;
+ d->suggestedVisibleFoldedBlockNumber = -1;
+ d->foldedBlockTimer.stop();
viewport()->update();
}
QPlainTextEdit::timerEvent(e);
}
-void BaseTextEditorPrivate::clearVisibleCollapsedBlock()
+void BaseTextEditorPrivate::clearVisibleFoldedBlock()
{
- if (suggestedVisibleCollapsedBlockNumber) {
- suggestedVisibleCollapsedBlockNumber = -1;
- collapsedBlockTimer.stop();
+ if (suggestedVisibleFoldedBlockNumber) {
+ suggestedVisibleFoldedBlockNumber = -1;
+ foldedBlockTimer.stop();
}
- if (visibleCollapsedBlockNumber >= 0) {
- visibleCollapsedBlockNumber = -1;
+ if (visibleFoldedBlockNumber >= 0) {
+ visibleFoldedBlockNumber = -1;
q->viewport()->update();
}
}
updateLink(e);
if (e->buttons() == Qt::NoButton) {
- const QTextBlock collapsedBlock = collapsedBlockAt(e->pos());
+ const QTextBlock collapsedBlock = foldedBlockAt(e->pos());
const int blockNumber = collapsedBlock.next().blockNumber();
if (blockNumber < 0) {
- d->clearVisibleCollapsedBlock();
- } else if (blockNumber != d->visibleCollapsedBlockNumber) {
- d->suggestedVisibleCollapsedBlockNumber = blockNumber;
- d->collapsedBlockTimer.start(40, this);
+ d->clearVisibleFoldedBlock();
+ } else if (blockNumber != d->visibleFoldedBlockNumber) {
+ d->suggestedVisibleFoldedBlockNumber = blockNumber;
+ d->foldedBlockTimer.start(40, this);
}
// Update the mouse cursor
- if (collapsedBlock.isValid() && !d->m_mouseOnCollapsedMarker) {
- d->m_mouseOnCollapsedMarker = true;
+ if (collapsedBlock.isValid() && !d->m_mouseOnFoldedMarker) {
+ d->m_mouseOnFoldedMarker = true;
viewport()->setCursor(Qt::PointingHandCursor);
- } else if (!collapsedBlock.isValid() && d->m_mouseOnCollapsedMarker) {
- d->m_mouseOnCollapsedMarker = false;
+ } else if (!collapsedBlock.isValid() && d->m_mouseOnFoldedMarker) {
+ d->m_mouseOnFoldedMarker = false;
viewport()->setCursor(Qt::IBeamCursor);
}
} else {
if (e->button() == Qt::LeftButton) {
d->clearBlockSelection(); // just in case, otherwise we might get strange drag and drop
- QTextBlock collapsedBlock = collapsedBlockAt(e->pos());
- if (collapsedBlock.isValid()) {
- toggleBlockVisible(collapsedBlock);
+ QTextBlock foldedBlock = foldedBlockAt(e->pos());
+ if (foldedBlock.isValid()) {
+ toggleBlockVisible(foldedBlock);
viewport()->setCursor(Qt::IBeamCursor);
}
void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e)
{
QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
- cursor.setPosition(cursor.block().position());
int markWidth;
extraAreaWidth(&markWidth);
if (d->m_codeFoldingVisible
&& e->type() == QEvent::MouseMove && e->buttons() == 0) { // mouse tracking
// Update which folder marker is highlighted
- const int highlightBlockNumber = d->extraAreaHighlightCollapseBlockNumber;
- const int highlightColumn = d->extraAreaHighlightCollapseColumn;
- d->extraAreaHighlightCollapseBlockNumber = -1;
- d->extraAreaHighlightCollapseColumn = -1;
-
- if (e->pos().x() > extraArea()->width() - collapseBoxWidth(fontMetrics())) {
- d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber();
- if (TextBlockUserData::canCollapse(cursor.block())
- || !TextBlockUserData::hasClosingCollapse(cursor.block()))
- d->extraAreaHighlightCollapseColumn = cursor.block().length()-1;
- if (TextBlockUserData::hasCollapseAfter(cursor.block())) {
- d->extraAreaHighlightCollapseBlockNumber++;
- d->extraAreaHighlightCollapseColumn = -1;
- if (TextBlockUserData::canCollapse(cursor.block().next())
- || !TextBlockUserData::hasClosingCollapse(cursor.block().next()))
- d->extraAreaHighlightCollapseColumn = cursor.block().next().length()-1;
- }
+ const int highlightBlockNumber = d->extraAreaHighlightFoldedBlockNumber;
+ d->extraAreaHighlightFoldedBlockNumber = -1;
+
+ if (e->pos().x() > extraArea()->width() - foldBoxWidth(fontMetrics())) {
+ d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
} else if (d->m_displaySettings.m_highlightBlocks) {
QTextCursor cursor = textCursor();
- d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber();
- d->extraAreaHighlightCollapseColumn = cursor.position() - cursor.block().position();
+ d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
}
- if (highlightBlockNumber != d->extraAreaHighlightCollapseBlockNumber
- || highlightColumn != d->extraAreaHighlightCollapseColumn) {
+ if (highlightBlockNumber != d->extraAreaHighlightFoldedBlockNumber)
d->m_highlightBlocksTimer->start(d->m_highlightBlocksInfo.isEmpty() ? 120 : 0);
}
- }
// Set whether the mouse cursor is a hand or normal arrow
if (e->type() == QEvent::MouseMove) {
if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) {
if (e->button() == Qt::LeftButton) {
- int boxWidth = collapseBoxWidth(fontMetrics());
+ int boxWidth = foldBoxWidth(fontMetrics());
if (d->m_codeFoldingVisible && e->pos().x() > extraArea()->width() - boxWidth) {
if (!cursor.block().next().isVisible()) {
toggleBlockVisible(cursor.block());
d->moveCursorVisible(false);
- } else if (collapseBox().contains(e->pos())) {
+ } else if (foldBox().contains(e->pos())) {
cursor.setPosition(
document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last()).position()
);
QTextBlock c = cursor.block();
- if (TextBlockUserData::hasCollapseAfter(c.previous()))
- c = c.previous();
toggleBlockVisible(c);
d->moveCursorVisible(false);
}
QTC_ASSERT(documentLayout, return);
bool visible = block.next().isVisible();
- TextBlockUserData::doCollapse(block, !visible);
+ BaseTextDocumentLayout::doFoldOrUnfold(block, !visible);
documentLayout->requestUpdate();
documentLayout->emitDocumentSizeChanged();
}
void BaseTextEditor::wheelEvent(QWheelEvent *e)
{
- d->clearVisibleCollapsedBlock();
+ d->clearVisibleFoldedBlock();
if (scrollWheelZoomingEnabled() && e->modifiers() & Qt::ControlModifier) {
const int delta = e->delta();
if (delta < 0)
void BaseTextEditor::zoomIn(int range)
{
- d->clearVisibleCollapsedBlock();
+ d->clearVisibleFoldedBlock();
emit requestFontZoom(range*10);
}
{
BaseTextEditorPrivateHighlightBlocks highlightBlocksInfo;
- if (d->extraAreaHighlightCollapseBlockNumber >= 0) {
- QTextBlock block = document()->findBlockByNumber(d->extraAreaHighlightCollapseBlockNumber);
- if (block.isValid()) {
- QTextCursor cursor(block);
- if (d->extraAreaHighlightCollapseColumn >= 0)
- cursor.setPosition(cursor.position() + qMin(d->extraAreaHighlightCollapseColumn,
- block.length()-1));
- QTextCursor closeCursor;
- bool firstRun = true;
- while (TextBlockUserData::findPreviousBlockOpenParenthesis(&cursor, firstRun)) {
- firstRun = false;
- highlightBlocksInfo.open.prepend(cursor.blockNumber());
- int visualIndent = d->visualIndent(cursor.block());
- if (closeCursor.isNull())
- closeCursor = cursor;
- if (TextBlockUserData::findNextBlockClosingParenthesis(&closeCursor)) {
- highlightBlocksInfo.close.append(closeCursor.blockNumber());
- visualIndent = qMin(visualIndent, d->visualIndent(closeCursor.block()));
- }
- highlightBlocksInfo.visualIndent.prepend(visualIndent);
+ QTextBlock block;
+ if (d->extraAreaHighlightFoldedBlockNumber >= 0) {
+ block = document()->findBlockByNumber(d->extraAreaHighlightFoldedBlockNumber);
+ if (block.isValid()
+ && block.next().isValid()
+ && BaseTextDocumentLayout::foldingIndent(block.next())
+ > BaseTextDocumentLayout::foldingIndent(block))
+ block = block.next();
+ }
+
+ QTextBlock closeBlock = block;
+ while (block.isValid()) {
+ int foldingIndent = BaseTextDocumentLayout::foldingIndent(block);
+
+ while (block.previous().isValid() && BaseTextDocumentLayout::foldingIndent(block) >= foldingIndent)
+ block = block.previous();
+ int nextIndent = BaseTextDocumentLayout::foldingIndent(block);
+ if (nextIndent == foldingIndent)
+ break;
+ highlightBlocksInfo.open.prepend(block.blockNumber());
+ while (closeBlock.next().isValid()
+ && BaseTextDocumentLayout::foldingIndent(closeBlock.next()) >= foldingIndent )
+ closeBlock = closeBlock.next();
+ highlightBlocksInfo.close.append(closeBlock.blockNumber());
+ int visualIndent = qMin(d->visualIndent(block), d->visualIndent(closeBlock));
+ highlightBlocksInfo.visualIndent.prepend(visualIndent);
+ }
+
+#if 0
+ if (block.isValid()) {
+ QTextCursor cursor(block);
+ if (d->extraAreaHighlightCollapseColumn >= 0)
+ cursor.setPosition(cursor.position() + qMin(d->extraAreaHighlightCollapseColumn,
+ block.length()-1));
+ QTextCursor closeCursor;
+ bool firstRun = true;
+ while (TextBlockUserData::findPreviousBlockOpenParenthesis(&cursor, firstRun)) {
+ firstRun = false;
+ highlightBlocksInfo.open.prepend(cursor.blockNumber());
+ int visualIndent = d->visualIndent(cursor.block());
+ if (closeCursor.isNull())
+ closeCursor = cursor;
+ if (TextBlockUserData::findNextBlockClosingParenthesis(&closeCursor)) {
+ highlightBlocksInfo.close.append(closeCursor.blockNumber());
+ visualIndent = qMin(visualIndent, d->visualIndent(closeCursor.block()));
}
+ highlightBlocksInfo.visualIndent.prepend(visualIndent);
}
}
-
+#endif
if (d->m_highlightBlocksInfo != highlightBlocksInfo) {
d->m_highlightBlocksInfo = highlightBlocksInfo;
viewport()->update();
braceDepthDelta -= delta;
}
- if (braceDepthDelta)
+ if (braceDepthDelta) {
BaseTextDocumentLayout::changeBraceDepth(block,braceDepthDelta);
+ BaseTextDocumentLayout::changeFoldingIndent(block, braceDepthDelta); // ### C++ only, refactor!
+ }
block = block.next();
}
d->m_displaySettings = ds;
if (!ds.m_highlightBlocks) {
- d->extraAreaHighlightCollapseBlockNumber = d->extraAreaHighlightCollapseColumn = -1;
+ d->extraAreaHighlightFoldedBlockNumber = -1;
d->m_highlightBlocksInfo = BaseTextEditorPrivateHighlightBlocks();
}
setAutoParenthesesEnabled(completionSettings.m_autoInsertBrackets);
}
-void BaseTextEditor::collapse()
+void BaseTextEditor::fold()
{
QTextDocument *doc = document();
BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
QTC_ASSERT(documentLayout, return);
QTextBlock block = textCursor().block();
- QTextBlock curBlock = block;
- while (block.isValid()) {
- if (TextBlockUserData::canCollapse(block) && block.next().isVisible()) {
- if (block == curBlock || block.next() == curBlock)
- break;
- if ((block.next().userState()) >> 8 <= (curBlock.previous().userState() >> 8))
- break;
- }
- block = block.previous();
+ if (!(BaseTextDocumentLayout::canFold(block) && block.next().isVisible())) {
+ // find the closest previous block which can fold
+ int indent = BaseTextDocumentLayout::foldingIndent(block);
+ while (block.isValid() && (BaseTextDocumentLayout::foldingIndent(block) >= indent || !block.isVisible()))
+ block = block.previous();
}
if (block.isValid()) {
- TextBlockUserData::doCollapse(block, false);
+ BaseTextDocumentLayout::doFoldOrUnfold(block, false);
d->moveCursorVisible();
documentLayout->requestUpdate();
documentLayout->emitDocumentSizeChanged();
}
}
-void BaseTextEditor::expand()
+void BaseTextEditor::unfold()
{
QTextDocument *doc = document();
BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
QTextBlock block = textCursor().block();
while (block.isValid() && !block.isVisible())
block = block.previous();
- TextBlockUserData::doCollapse(block, true);
+ BaseTextDocumentLayout::doFoldOrUnfold(block, true);
d->moveCursorVisible();
documentLayout->requestUpdate();
documentLayout->emitDocumentSizeChanged();
}
-void BaseTextEditor::unCollapseAll()
+void BaseTextEditor::unfoldAll()
{
QTextDocument *doc = document();
BaseTextDocumentLayout *documentLayout = qobject_cast<BaseTextDocumentLayout*>(doc->documentLayout());
QTextBlock block = doc->firstBlock();
bool makeVisible = true;
while (block.isValid()) {
- if (block.isVisible() && TextBlockUserData::canCollapse(block) && block.next().isVisible()) {
+ if (block.isVisible() && BaseTextDocumentLayout::canFold(block) && block.next().isVisible()) {
makeVisible = false;
break;
}
block = doc->firstBlock();
while (block.isValid()) {
- if (TextBlockUserData::canCollapse(block))
- TextBlockUserData::doCollapse(block, makeVisible);
+ if (BaseTextDocumentLayout::canFold(block))
+ BaseTextDocumentLayout::doFoldOrUnfold(block, makeVisible);
block = block.next();
}
void cutLine();
void deleteLine();
- void unCollapseAll();
- void collapse();
- void expand();
+ void unfoldAll();
+ void fold();
+ void unfold();
void selectEncoding();
void gotoBlockStart();
void joinLines();
+ void insertLineAbove();
+ void insertLineBelow();
+
void cleanWhitespace();
signals:
bool hovered) const;
void toggleBlockVisible(const QTextBlock &block);
- QRect collapseBox();
+ QRect foldBox();
- QTextBlock collapsedBlockAt(const QPoint &pos, QRect *box = 0) const;
+ QTextBlock foldedBlockAt(const QPoint &pos, QRect *box = 0) const;
void updateLink(QMouseEvent *e);
void showLink(const Link &);
int extraAreaSelectionAnchorBlockNumber;
int extraAreaToggleMarkBlockNumber;
- int extraAreaHighlightCollapseBlockNumber;
- int extraAreaHighlightCollapseColumn;
+ int extraAreaHighlightFoldedBlockNumber;
TextEditorOverlay *m_overlay;
TextEditorOverlay *m_snippetOverlay;
QTextCharFormat m_occurrencesFormat;
QTextCharFormat m_occurrenceRenameFormat;
- QBasicTimer collapsedBlockTimer;
- int visibleCollapsedBlockNumber;
- int suggestedVisibleCollapsedBlockNumber;
- void clearVisibleCollapsedBlock();
- bool m_mouseOnCollapsedMarker;
- void collapseLicenseHeader();
+ QBasicTimer foldedBlockTimer;
+ int visibleFoldedBlockNumber;
+ int suggestedVisibleFoldedBlockNumber;
+ void clearVisibleFoldedBlock();
+ bool m_mouseOnFoldedMarker;
+ void foldLicenseHeader();
QBasicTimer autoScrollTimer;
void updateMarksLineNumber();
qreal smallRatio;
qreal largeRatio;
if (m_id == QLatin1String(Constants::C_CURRENT_LINE)) {
- smallRatio = .15;
- largeRatio = .3;
+ smallRatio = .3;
+ largeRatio = .6;
} else {
smallRatio = .05;
largeRatio = .4;
--- /dev/null
+/**************************************************************************
+**
+** 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 "context.h"
+#include "rule.h"
+#include "reuse.h"
+#include "dynamicrule.h"
+#include "highlightdefinition.h"
+
+using namespace TextEditor;
+using namespace Internal;
+
+Context::Context() : m_fallthrough(false), m_dynamic(false)
+{}
+
+Context::Context(const Context &context) :
+ m_id(context.m_id), m_name(context.m_name), m_lineBeginContext(context.m_lineBeginContext),
+ m_lineEndContext(context.m_lineEndContext), m_fallthroughContext(context.m_fallthroughContext),
+ m_itemData(context.m_itemData), m_fallthrough(context.m_fallthrough),
+ m_dynamic(context.m_dynamic), m_instructions(context.m_instructions),
+ m_definition(context.m_definition)
+{
+ // Rules need to be deeply copied because of dynamic contexts.
+ foreach (const QSharedPointer<Rule> &rule, context.m_rules)
+ m_rules.append(QSharedPointer<Rule>(rule->clone()));
+}
+
+const Context &Context::operator=(Context copy)
+{
+ swap(copy);
+ return *this;
+}
+
+Context::~Context()
+{}
+
+void Context::swap(Context &context)
+{
+ qSwap(m_id, context.m_id);
+ qSwap(m_name, context.m_name);
+ qSwap(m_lineBeginContext, context.m_lineBeginContext);
+ qSwap(m_lineEndContext, context.m_lineEndContext);
+ qSwap(m_fallthroughContext, context.m_fallthroughContext);
+ qSwap(m_itemData, context.m_itemData);
+ qSwap(m_fallthrough, context.m_fallthrough);
+ qSwap(m_dynamic, context.m_dynamic);
+ qSwap(m_rules, context.m_rules);
+ qSwap(m_instructions, context.m_instructions);
+ qSwap(m_definition, context.m_definition);
+}
+
+void Context::configureId(const int unique)
+{ m_id.append(QString::number(unique)); }
+
+const QString &Context::id() const
+{ return m_id; }
+
+void Context::setName(const QString &name)
+{
+ m_name = name;
+ m_id = name;
+}
+
+const QString &Context::name() const
+{ return m_name; }
+
+void Context::setLineBeginContext(const QString &context)
+{ m_lineBeginContext = context; }
+
+const QString &Context::lineBeginContext() const
+{ return m_lineBeginContext; }
+
+void Context::setLineEndContext(const QString &context)
+{ m_lineEndContext = context; }
+
+const QString &Context::lineEndContext() const
+{ return m_lineEndContext; }
+
+void Context::setFallthroughContext(const QString &context)
+{ m_fallthroughContext = context; }
+
+const QString &Context::fallthroughContext() const
+{ return m_fallthroughContext; }
+
+void Context::setItemData(const QString &itemData)
+{ m_itemData = itemData; }
+
+const QString &Context::itemData() const
+{ return m_itemData; }
+
+void Context::setFallthrough(const QString &fallthrough)
+{ m_fallthrough = toBool(fallthrough); }
+
+bool Context::isFallthrough() const
+{ return m_fallthrough; }
+
+void Context::setDynamic(const QString &dynamic)
+{ m_dynamic = toBool(dynamic); }
+
+bool Context::isDynamic() const
+{ return m_dynamic; }
+
+void Context::updateDynamicRules(const QStringList &captures) const
+{
+ TextEditor::Internal::updateDynamicRules(m_rules, captures);
+}
+
+void Context::addRule(const QSharedPointer<Rule> &rule)
+{ m_rules.append(rule); }
+
+void Context::addRule(const QSharedPointer<Rule> &rule, int index)
+{ m_rules.insert(index, rule); }
+
+const QList<QSharedPointer<Rule> > & Context::rules() const
+{ return m_rules; }
+
+void Context::addIncludeRulesInstruction(const IncludeRulesInstruction &instruction)
+{ m_instructions.append(instruction); }
+
+const QList<IncludeRulesInstruction> &Context::includeRulesInstructions() const
+{ return m_instructions; }
+
+void Context::clearIncludeRulesInstructions()
+{ m_instructions.clear(); }
+
+void Context::setDefinition(const QSharedPointer<HighlightDefinition> &definition)
+{ m_definition = definition; }
+
+const QSharedPointer<HighlightDefinition> &Context::definition() const
+{ return m_definition; }
--- /dev/null
+/**************************************************************************
+**
+** 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 CONTEXT_H
+#define CONTEXT_H
+
+#include "includerulesinstruction.h"
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtCore/QSharedPointer>
+
+namespace TextEditor {
+namespace Internal {
+
+class Rule;
+class HighlightDefinition;
+
+class Context
+{
+public:
+ Context();
+ Context(const Context &context);
+ const Context &operator=(Context copy);
+ ~Context();
+
+ void configureId(const int unique);
+ const QString &id() const;
+
+ void setName(const QString &name);
+ const QString &name() const;
+
+ void setLineBeginContext(const QString &context);
+ const QString &lineBeginContext() const;
+
+ void setLineEndContext(const QString &context);
+ const QString &lineEndContext() const;
+
+ void setFallthroughContext(const QString &context);
+ const QString &fallthroughContext() const;
+
+ void setItemData(const QString &itemData);
+ const QString &itemData() const;
+
+ void setFallthrough(const QString &fallthrough);
+ bool isFallthrough() const;
+
+ void setDynamic(const QString &dynamic);
+ bool isDynamic() const;
+ void updateDynamicRules(const QStringList &captures) const;
+
+ void addRule(const QSharedPointer<Rule> &rule);
+ void addRule(const QSharedPointer<Rule> &rule, int index);
+ const QList<QSharedPointer<Rule> > &rules() const;
+
+ void addIncludeRulesInstruction(const IncludeRulesInstruction &instruction);
+ const QList<IncludeRulesInstruction> &includeRulesInstructions() const;
+ void clearIncludeRulesInstructions();
+
+ void setDefinition(const QSharedPointer<HighlightDefinition> &definition);
+ const QSharedPointer<HighlightDefinition> &definition() const;
+
+ void swap(Context &context);
+
+private:
+ QString m_id;
+ QString m_name;
+ QString m_lineBeginContext;
+ QString m_lineEndContext;
+ QString m_fallthroughContext;
+ QString m_itemData;
+ bool m_fallthrough;
+ bool m_dynamic;
+
+ QList<QSharedPointer<Rule> > m_rules;
+ QList<IncludeRulesInstruction> m_instructions;
+
+ QSharedPointer<HighlightDefinition> m_definition;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // CONTEXT_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "dynamicrule.h"
+#include "reuse.h"
+
+using namespace TextEditor;
+using namespace Internal;
+
+DynamicRule::DynamicRule() : m_active(false)
+{}
+
+DynamicRule::~DynamicRule()
+{}
+
+void DynamicRule::setActive(const QString &active)
+{ m_active = toBool(active); }
+
+bool DynamicRule::isActive() const
+{ return m_active; }
+
+void DynamicRule::replaceExpressions(const QStringList &captures)
+{
+ doReplaceExpressions(captures);
+ updateDynamicRules(childs(), captures);
+}
+
+namespace TextEditor {
+namespace Internal {
+
+void updateDynamicRules(const QList<QSharedPointer<Rule> > &rules, const QStringList &captures)
+{
+ foreach (QSharedPointer<Rule> rule, rules) {
+ DynamicRule *dynamicRule = dynamic_cast<DynamicRule *>(rule.data());
+ if (dynamicRule && dynamicRule->isActive())
+ dynamicRule->replaceExpressions(captures);
+ }
+}
+
+} // namespace Internal
+} // namespace TextEditor
--- /dev/null
+/**************************************************************************
+**
+** 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 DYNAMICRULE_H
+#define DYNAMICRULE_H
+
+#include "rule.h"
+
+QT_BEGIN_NAMESPACE
+class QStringList;
+QT_END_NAMESPACE
+
+namespace TextEditor {
+namespace Internal {
+
+class DynamicRule : public Rule
+{
+public:
+ DynamicRule();
+ virtual ~DynamicRule();
+
+ void setActive(const QString &active);
+ bool isActive() const;
+
+ virtual void replaceExpressions(const QStringList &captures);
+
+private:
+ virtual void doReplaceExpressions(const QStringList &captures) = 0;
+
+ bool m_active;
+};
+
+void updateDynamicRules(const QList<QSharedPointer<Rule> > &rules, const QStringList &captures);
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // DYNAMICRULE_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "highlightdefinition.h"
+#include "highlighterexception.h"
+#include "context.h"
+#include "keywordlist.h"
+#include "itemdata.h"
+#include "reuse.h"
+
+#include <QLatin1String>
+
+using namespace TextEditor;
+using namespace Internal;
+
+HighlightDefinition::HighlightDefinition() :
+ m_delimiters(QLatin1String(".():!+,-<=>%&/;?[]^{|}~\\*, \t")),
+ m_singleLineCommentAfterWhiteSpaces(false),
+ m_keywordCaseSensitivity(Qt::CaseSensitive)
+{}
+
+HighlightDefinition::~HighlightDefinition()
+{}
+
+template <class Element, class Container>
+const QSharedPointer<Element> &HighlightDefinition::
+GenericHelper::create(const QString &name, Container &container)
+{
+ if (name.isEmpty())
+ throw HighlighterException();
+
+ if (container.contains(name))
+ throw HighlighterException();
+
+ container.insert(name, QSharedPointer<Element>(new Element));
+ return *container.constFind(name);
+}
+
+template <class Element, class Container>
+const QSharedPointer<Element> &HighlightDefinition::
+GenericHelper::find(const QString &name, const Container &container) const
+{
+ typename Container::const_iterator it = container.find(name);
+ if (it == container.end())
+ throw HighlighterException();
+
+ return it.value();
+}
+
+const QSharedPointer<KeywordList> &HighlightDefinition::createKeywordList(const QString &list)
+{ return m_helper.create<KeywordList>(list, m_lists); }
+
+const QSharedPointer<KeywordList> &HighlightDefinition::keywordList(const QString &list)
+{ return m_helper.find<KeywordList>(list, m_lists); }
+
+const QSharedPointer<Context> &HighlightDefinition::createContext(const QString &context,
+ bool initial)
+{
+ if (initial)
+ m_initialContext = context;
+
+ return m_helper.create<Context>(context, m_contexts);
+}
+
+const QSharedPointer<Context> &HighlightDefinition::initialContext() const
+{ return m_helper.find<Context>(m_initialContext, m_contexts); }
+
+const QSharedPointer<Context> &HighlightDefinition::context(const QString &context) const
+{ return m_helper.find<Context>(context, m_contexts); }
+
+const QHash<QString, QSharedPointer<Context> > &HighlightDefinition::contexts() const
+{ return m_contexts; }
+
+const QSharedPointer<ItemData> &HighlightDefinition::createItemData(const QString &itemData)
+{ return m_helper.create<ItemData>(itemData, m_itemsData); }
+
+const QSharedPointer<ItemData> &HighlightDefinition::itemData(const QString &itemData) const
+{ return m_helper.find<ItemData>(itemData, m_itemsData); }
+
+void HighlightDefinition::setSingleLineComment(const QString &start)
+{ m_singleLineComment = start; }
+
+const QString &HighlightDefinition::singleLineComment() const
+{ return m_singleLineComment; }
+
+void HighlightDefinition::setCommentAfterWhitespaces(const QString &after)
+{
+ if (after == QLatin1String("afterwhitespace"))
+ m_singleLineCommentAfterWhiteSpaces = true;
+}
+
+bool HighlightDefinition::isCommentAfterWhiteSpaces() const
+{ return m_singleLineCommentAfterWhiteSpaces; }
+
+void HighlightDefinition::setMultiLineCommentStart(const QString &start)
+{ m_multiLineCommentStart = start; }
+
+const QString &HighlightDefinition::multiLineCommentStart() const
+{ return m_multiLineCommentStart; }
+
+void HighlightDefinition::setMultiLineCommentEnd(const QString &end)
+{ m_multiLineCommentEnd = end; }
+
+const QString &HighlightDefinition::multiLineCommentEnd() const
+{ return m_multiLineCommentEnd; }
+
+void HighlightDefinition::setMultiLineCommentRegion(const QString ®ion)
+{ m_multiLineCommentRegion = region; }
+
+const QString &HighlightDefinition::multiLineCommentRegion() const
+{ return m_multiLineCommentRegion; }
+
+void HighlightDefinition::removeDelimiters(const QString &characters)
+{
+ for (int i = 0; i < characters.length(); ++i)
+ m_delimiters.remove(characters.at(i));
+}
+
+void HighlightDefinition::addDelimiters(const QString &characters)
+{
+ for (int i = 0; i < characters.length(); ++i) {
+ if (!m_delimiters.contains(characters.at(i)))
+ m_delimiters.append(characters.at(i));
+ }
+}
+
+bool HighlightDefinition::isDelimiter(const QChar &character) const
+{
+ if (m_delimiters.contains(character))
+ return true;
+ return false;
+}
+
+void HighlightDefinition::setKeywordsSensitive(const QString &sensitivity)
+{
+ if (!sensitivity.isEmpty())
+ m_keywordCaseSensitivity = toCaseSensitivity(toBool(sensitivity));
+}
+
+Qt::CaseSensitivity HighlightDefinition::keywordsSensitive() const
+{ return m_keywordCaseSensitivity; }
--- /dev/null
+/**************************************************************************
+**
+** 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 HIGHLIGHTDEFINITION_H
+#define HIGHLIGHTDEFINITION_H
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QSharedPointer>
+
+namespace TextEditor {
+namespace Internal {
+
+class KeywordList;
+class Context;
+class ItemData;
+
+class HighlightDefinition
+{
+public:
+ HighlightDefinition();
+ ~HighlightDefinition();
+
+ const QSharedPointer<KeywordList> &createKeywordList(const QString &list);
+ const QSharedPointer<KeywordList> &keywordList(const QString &list);
+
+ const QSharedPointer<Context> &createContext(const QString &context, bool initial);
+ const QSharedPointer<Context> &initialContext() const;
+ const QSharedPointer<Context> &context(const QString &context) const;
+ const QHash<QString, QSharedPointer<Context> > &contexts() const;
+
+ const QSharedPointer<ItemData> &createItemData(const QString &itemData);
+ const QSharedPointer<ItemData> &itemData(const QString &itemData) const;
+
+ void setKeywordsSensitive(const QString &sensitivity);
+ Qt::CaseSensitivity keywordsSensitive() const;
+
+ void addDelimiters(const QString &characters);
+ void removeDelimiters(const QString &characters);
+ bool isDelimiter(const QChar &character) const;
+
+ void setSingleLineComment(const QString &start);
+ const QString &singleLineComment() const;
+
+ void setCommentAfterWhitespaces(const QString &after);
+ bool isCommentAfterWhiteSpaces() const;
+
+ void setMultiLineCommentStart(const QString &start);
+ const QString &multiLineCommentStart() const;
+
+ void setMultiLineCommentEnd(const QString &end);
+ const QString &multiLineCommentEnd() const;
+
+ void setMultiLineCommentRegion(const QString ®ion);
+ const QString &multiLineCommentRegion() const;
+
+private:
+ Q_DISABLE_COPY(HighlightDefinition)
+
+ struct GenericHelper
+ {
+ template <class Element, class Container>
+ const QSharedPointer<Element> &create(const QString &name, Container &container);
+
+ template <class Element, class Container>
+ const QSharedPointer<Element> &find(const QString &name, const Container &container) const;
+ };
+ GenericHelper m_helper;
+
+ QHash<QString, QSharedPointer<KeywordList> > m_lists;
+ QHash<QString, QSharedPointer<Context> > m_contexts;
+ QHash<QString, QSharedPointer<ItemData> > m_itemsData;
+
+ QString m_initialContext;
+
+ QString m_delimiters;
+
+ QString m_singleLineComment;
+ bool m_singleLineCommentAfterWhiteSpaces;
+
+ QString m_multiLineCommentStart;
+ QString m_multiLineCommentEnd;
+ QString m_multiLineCommentRegion;
+
+ Qt::CaseSensitivity m_keywordCaseSensitivity;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // HIGHLIGHTDEFINITION_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "highlightdefinitionhandler.h"
+#include "highlightdefinition.h"
+#include "specificrules.h"
+#include "itemdata.h"
+#include "keywordlist.h"
+#include "context.h"
+#include "reuse.h"
+#include "manager.h"
+#include "highlighterexception.h"
+
+#include <QLatin1String>
+
+using namespace TextEditor;
+using namespace Internal;
+
+namespace {
+ static const QLatin1String kName("name");
+ static const QLatin1String kList("list");
+ static const QLatin1String kItem("item");
+ static const QLatin1String kContext("context");
+ static const QLatin1String kAttribute("attribute");
+ static const QLatin1String kDynamic("dynamic");
+ static const QLatin1String kFallthrough("fallthrough");
+ static const QLatin1String kLineEndContext("lineEndContext");
+ static const QLatin1String kLineBeginContext("lineBeginContext");
+ static const QLatin1String kFallthroughContext("fallthroughContext");
+ static const QLatin1String kBeginRegion("beginRegion");
+ static const QLatin1String kEndRegion("endRegion");
+ static const QLatin1String kLookAhead("lookAhead");
+ static const QLatin1String kFirstNonSpace("firstNonSpace");
+ static const QLatin1String kColumn("column");
+ static const QLatin1String kItemData("itemData");
+ static const QLatin1String kDefStyleNum("defStyleNum");
+ static const QLatin1String kColor("color");
+ static const QLatin1String kSelColor("selColor");
+ static const QLatin1String kItalic("italic");
+ static const QLatin1String kBold("bold");
+ static const QLatin1String kUnderline("underline");
+ static const QLatin1String kStrikeout("strikeout");
+ static const QLatin1String kChar("char");
+ static const QLatin1String kChar1("char1");
+ static const QLatin1String kString("String");
+ static const QLatin1String kInsensitive("insensitive");
+ static const QLatin1String kMinimal("minimal");
+ static const QLatin1String kKeywords("keywords");
+ static const QLatin1String kCaseSensitive("casesensitive");
+ static const QLatin1String kWeakDeliminator("weakDeliminator");
+ static const QLatin1String kAdditionalDeliminator("additionalDeliminator");
+ static const QLatin1String kWordWrapDeliminator("wordWrapDeliminator");
+ static const QLatin1String kComment("comment");
+ static const QLatin1String kPosition("position");
+ static const QLatin1String kSingleLine("singleline");
+ static const QLatin1String kMultiLine("multiline");
+ static const QLatin1String kStart("start");
+ static const QLatin1String kEnd("end");
+ static const QLatin1String kRegion("region");
+ static const QLatin1String kDetectChar("DetectChar");
+ static const QLatin1String kDetect2Chars("Detect2Chars");
+ static const QLatin1String kAnyChar("AnyChar");
+ static const QLatin1String kStringDetect("StringDetect");
+ static const QLatin1String kRegExpr("RegExpr");
+ static const QLatin1String kKeyword("keyword");
+ static const QLatin1String kInt("Int");
+ static const QLatin1String kFloat("Float");
+ static const QLatin1String kHlCOct("HlCOct");
+ static const QLatin1String kHlCHex("HlCHex");
+ static const QLatin1String kHlCStringChar("HlCStringChar");
+ static const QLatin1String kHlCChar("HlCChar");
+ static const QLatin1String kRangeDetect("RangeDetect");
+ static const QLatin1String kLineContinue("LineContinue");
+ static const QLatin1String kIncludeRules("IncludeRules");
+ static const QLatin1String kDetectSpaces("DetectSpaces");
+ static const QLatin1String kDetectIdentifier("DetectIdentifier");
+ static const QLatin1String kLanguage("language");
+ static const QLatin1String kExtensions("extensions");
+ static const QLatin1String kIncludeAttrib("includeAttrib");
+ static const QLatin1String kHash("#");
+ static const QLatin1String kDoubleHash("##");
+}
+
+HighlightDefinitionHandler::
+HighlightDefinitionHandler(const QSharedPointer<HighlightDefinition> &definition) :
+ m_definition(definition),
+ m_processingKeyword(false),
+ m_initialContext(true)
+{}
+
+HighlightDefinitionHandler::~HighlightDefinitionHandler()
+{}
+
+bool HighlightDefinitionHandler::startDocument()
+{
+ return true;
+}
+
+bool HighlightDefinitionHandler::endDocument()
+{
+ processIncludeRules();
+ return true;
+}
+
+bool HighlightDefinitionHandler::startElement(const QString &,
+ const QString &,
+ const QString &qName,
+ const QXmlAttributes &atts)
+{
+ if (qName == kList) {
+ listElementStarted(atts);
+ } else if (qName == kItem) {
+ itemElementStarted();
+ } else if (qName == kContext) {
+ contextElementStarted(atts);
+ } else if (qName == kItemData) {
+ itemDataElementStarted(atts);
+ } else if (qName == kComment) {
+ commentElementStarted(atts);
+ } else if (qName == kKeywords) {
+ keywordsElementStarted(atts);
+ } else if (qName == kDetectChar) {
+ detectCharStarted(atts);
+ } else if (qName == kDetect2Chars) {
+ detect2CharsStarted(atts);
+ } else if (qName == kAnyChar) {
+ anyCharStarted(atts);
+ } else if (qName == kStringDetect) {
+ stringDetectedStarted(atts);
+ } else if (qName == kRegExpr) {
+ regExprStarted(atts);
+ } else if (qName == kKeyword) {
+ keywordStarted(atts);
+ } else if (qName == kInt) {
+ intStarted(atts);
+ } else if (qName == kFloat) {
+ floatStarted(atts);
+ } else if (qName == kHlCOct) {
+ hlCOctStarted(atts);
+ } else if (qName == kHlCHex) {
+ hlCHexStarted(atts);
+ } else if (qName == kHlCStringChar) {
+ hlCStringCharStarted(atts);
+ } else if (qName == kHlCChar) {
+ hlCCharStarted(atts);
+ } else if (qName == kRangeDetect) {
+ rangeDetectStarted(atts);
+ } else if (qName == kLineContinue) {
+ lineContinue(atts);
+ } else if (qName == kIncludeRules) {
+ includeRulesStarted(atts);
+ } else if (qName == kDetectSpaces) {
+ detectSpacesStarted(atts);
+ } else if (qName == kDetectIdentifier) {
+ detectIdentifier(atts);
+ }
+
+ return true;
+}
+
+bool HighlightDefinitionHandler::endElement(const QString &, const QString &, const QString &qName)
+{
+ if (qName == kItem) {
+ m_currentList->addKeyword(m_currentKeyword.trimmed());
+ m_processingKeyword = false;
+ } else if (qName == kDetectChar || qName == kDetect2Chars || qName == kAnyChar ||
+ qName == kStringDetect || qName == kRegExpr || qName == kKeyword || qName == kInt ||
+ qName == kFloat || qName == kHlCOct || qName == kHlCHex || qName == kHlCStringChar ||
+ qName == kHlCChar || qName == kRangeDetect || qName == kLineContinue ||
+ qName == kDetectSpaces || qName == kDetectIdentifier) {
+ m_currentRule.pop();
+ }
+
+ return true;
+}
+
+bool HighlightDefinitionHandler::characters(const QString& ch)
+{
+ // Character data of an element may be reported in more than one chunk.
+ if (m_processingKeyword)
+ m_currentKeyword.append(ch);
+
+ return true;
+}
+
+void HighlightDefinitionHandler::listElementStarted(const QXmlAttributes &atts)
+{
+ m_currentList = m_definition->createKeywordList(atts.value(kName));
+}
+
+void HighlightDefinitionHandler::itemElementStarted()
+{
+ m_currentKeyword.clear();
+ m_processingKeyword = true;
+}
+
+void HighlightDefinitionHandler::contextElementStarted(const QXmlAttributes &atts)
+{
+ m_currentContext = m_definition->createContext(atts.value(kName), m_initialContext);
+ m_currentContext->setDefinition(m_definition);
+ m_currentContext->setName(atts.value(kName));
+ m_currentContext->setItemData(atts.value(kAttribute));
+ m_currentContext->setDynamic(atts.value(kDynamic));
+ m_currentContext->setFallthrough(atts.value(kFallthrough));
+ m_currentContext->setFallthroughContext(atts.value(kFallthroughContext));
+ m_currentContext->setLineBeginContext(atts.value(kLineBeginContext));
+ m_currentContext->setLineEndContext(atts.value(kLineEndContext));
+
+ m_initialContext = false;
+}
+
+void HighlightDefinitionHandler::ruleElementStarted(const QXmlAttributes &atts,
+ const QSharedPointer<Rule> &rule)
+{
+ // The definition of a rule is not necessarily the same of its enclosing context because of
+ // externally included rules.
+ rule->setDefinition(m_definition);
+ rule->setItemData(atts.value(kAttribute));
+ rule->setContext(atts.value(kContext));
+ rule->setBeginRegion(atts.value(kBeginRegion));
+ rule->setEndRegion(atts.value(kEndRegion));
+ rule->setLookAhead(atts.value(kLookAhead));
+ rule->setFirstNonSpace(atts.value(kFirstNonSpace));
+ rule->setColumn(atts.value(kColumn));
+
+ if (m_currentRule.isEmpty())
+ m_currentContext->addRule(rule);
+ else
+ m_currentRule.top()->addChild(rule);
+
+ m_currentRule.push(rule);
+}
+
+void HighlightDefinitionHandler::itemDataElementStarted(const QXmlAttributes &atts) const
+{
+ QSharedPointer<ItemData> itemData = m_definition->createItemData(atts.value(kName));
+ itemData->setStyle(atts.value(kDefStyleNum));
+ itemData->setColor(atts.value(kColor));
+ itemData->setSelectionColor(atts.value(kSelColor));
+ itemData->setItalic(atts.value(kItalic));
+ itemData->setBold(atts.value(kBold));
+ itemData->setUnderlined(atts.value(kUnderline));
+ itemData->setStrikedOut(atts.value(kStrikeout));
+}
+
+void HighlightDefinitionHandler::commentElementStarted(const QXmlAttributes &atts) const
+{
+ const QString &commentType = atts.value(kName);
+ if (commentType.compare(kSingleLine, Qt::CaseInsensitive) == 0) {
+ m_definition->setSingleLineComment(atts.value(kStart));
+ m_definition->setCommentAfterWhitespaces(atts.value(kPosition));
+ } else if (commentType.compare(kMultiLine, Qt::CaseInsensitive) == 0) {
+ m_definition->setMultiLineCommentStart(atts.value(kStart));
+ m_definition->setMultiLineCommentEnd(atts.value(kEnd));
+ m_definition->setMultiLineCommentRegion(atts.value(kRegion));
+ }
+}
+
+void HighlightDefinitionHandler::keywordsElementStarted(const QXmlAttributes &atts) const
+{
+ // Global case sensitivity appears last in the document (required by dtd) and is set here.
+ m_definition->setKeywordsSensitive(atts.value(kCaseSensitive));
+ m_definition->removeDelimiters(atts.value(kWeakDeliminator));
+ m_definition->addDelimiters(atts.value(kAdditionalDeliminator));
+ //@todo: wordWrapDelimiters?
+}
+
+void HighlightDefinitionHandler::detectCharStarted(const QXmlAttributes &atts)
+{
+ DetectCharRule *rule = new DetectCharRule;
+ rule->setChar(atts.value(kChar));
+ rule->setActive(atts.value(kDynamic));
+ ruleElementStarted(atts, QSharedPointer<Rule>(rule));
+}
+
+void HighlightDefinitionHandler::detect2CharsStarted(const QXmlAttributes &atts)
+{
+ Detect2CharsRule *rule = new Detect2CharsRule;
+ rule->setChar(atts.value(kChar));
+ rule->setChar1(atts.value(kChar1));
+ rule->setActive(atts.value(kDynamic));
+ ruleElementStarted(atts, QSharedPointer<Rule>(rule));
+}
+
+void HighlightDefinitionHandler::anyCharStarted(const QXmlAttributes &atts)
+{
+ AnyCharRule *rule = new AnyCharRule;
+ rule->setCharacterSet(atts.value(kString));
+ ruleElementStarted(atts, QSharedPointer<Rule>(rule));
+}
+
+void HighlightDefinitionHandler::stringDetectedStarted(const QXmlAttributes &atts)
+{
+ StringDetectRule *rule = new StringDetectRule;
+ rule->setString(atts.value(kString));
+ rule->setInsensitive(atts.value(kInsensitive));
+ rule->setActive(atts.value(kDynamic));
+ ruleElementStarted(atts, QSharedPointer<Rule>(rule));
+}
+
+void HighlightDefinitionHandler::regExprStarted(const QXmlAttributes &atts)
+{
+ RegExprRule *rule = new RegExprRule;
+ rule->setPattern(atts.value(kString));
+ rule->setMinimal(atts.value(kMinimal));
+ rule->setInsensitive(atts.value(kInsensitive));
+ rule->setActive(atts.value(kDynamic));
+ ruleElementStarted(atts, QSharedPointer<Rule>(rule));
+}
+
+void HighlightDefinitionHandler::keywordStarted(const QXmlAttributes &atts)
+{
+ KeywordRule *rule = new KeywordRule(m_definition);
+ rule->setList(atts.value(kString));
+ rule->setInsensitive(atts.value(kInsensitive));
+ ruleElementStarted(atts, QSharedPointer<Rule>(rule));
+}
+
+void HighlightDefinitionHandler::intStarted(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new IntRule));
+}
+
+void HighlightDefinitionHandler::floatStarted(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new FloatRule));
+}
+
+void HighlightDefinitionHandler::hlCOctStarted(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new HlCOctRule));
+}
+
+void HighlightDefinitionHandler::hlCHexStarted(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new HlCHexRule));
+}
+
+void HighlightDefinitionHandler::hlCStringCharStarted(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new HlCStringCharRule));
+}
+
+void HighlightDefinitionHandler::hlCCharStarted(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new HlCCharRule));
+}
+
+void HighlightDefinitionHandler::rangeDetectStarted(const QXmlAttributes &atts)
+{
+ RangeDetectRule *rule = new RangeDetectRule;
+ rule->setChar(atts.value(kChar));
+ rule->setChar1(atts.value(kChar1));
+ ruleElementStarted(atts, QSharedPointer<Rule>(rule));
+}
+
+void HighlightDefinitionHandler::lineContinue(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new LineContinueRule));
+}
+
+void HighlightDefinitionHandler::includeRulesStarted(const QXmlAttributes &atts)
+{
+ // Include rules are treated as instructions for latter processing.
+ IncludeRulesInstruction instruction(atts.value(kContext), m_currentContext->rules().size(),
+ atts.value(kIncludeAttrib));
+
+ // Include rules (as many others) are not allowed as a child.
+ m_currentContext->addIncludeRulesInstruction(instruction);
+}
+
+void HighlightDefinitionHandler::detectSpacesStarted(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new DetectSpacesRule));
+}
+
+void HighlightDefinitionHandler::detectIdentifier(const QXmlAttributes &atts)
+{
+ ruleElementStarted(atts, QSharedPointer<Rule>(new DetectIdentifierRule));
+}
+
+void HighlightDefinitionHandler::processIncludeRules() const
+{
+ const QHash<QString, QSharedPointer<Context> > &allContexts = m_definition->contexts();
+ foreach (const QSharedPointer<Context> &context, allContexts)
+ processIncludeRules(context);
+}
+
+void HighlightDefinitionHandler::processIncludeRules(const QSharedPointer<Context> &context) const
+{
+ if (context->includeRulesInstructions().isEmpty())
+ return;
+
+ int rulesIncluded = 0;
+ const QList<IncludeRulesInstruction> &instructions = context->includeRulesInstructions();
+ foreach (const IncludeRulesInstruction &instruction, instructions) {
+
+ QSharedPointer<Context> sourceContext;
+ const QString &sourceName = instruction.sourceContext();
+ if (sourceName.startsWith(kDoubleHash)) {
+ // This refers to an external definition. The rules included are the ones from its
+ // initial context. Others contexts and rules from the external definition will work
+ // transparently to the highlighter. This is because contexts and rules know the
+ // definition they are from.
+ QString externalName = QString::fromRawData(sourceName.unicode() + 2,
+ sourceName.length() - 2);
+ const QString &id = Manager::instance()->definitionIdByName(externalName);
+
+ // If there is an incorrect circular dependency among definitions this is skipped.
+ if (Manager::instance()->isBuildingDefinition(id))
+ continue;
+
+ const QSharedPointer<HighlightDefinition> &externalDefinition =
+ Manager::instance()->definition(id);
+ sourceContext = externalDefinition->initialContext();
+ } else if (!sourceName.startsWith(kHash)) {
+ sourceContext = m_definition->context(sourceName);
+
+ // Recursion is done only for context direct rules. Child rules are not processed
+ // because they cannot be include rules.
+ processIncludeRules(sourceContext);
+ }
+
+ if (instruction.replaceItemData()) {
+ context->setItemData(sourceContext->itemData());
+ context->setDefinition(sourceContext->definition());
+ }
+
+ foreach (QSharedPointer<Rule> rule, sourceContext->rules()) {
+ context->addRule(rule, instruction.indexHint() + rulesIncluded);
+ ++rulesIncluded;
+ }
+ }
+ context->clearIncludeRulesInstructions();
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 HIGHLIGHTDEFINITIONHANDLER_H
+#define HIGHLIGHTDEFINITIONHANDLER_H
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QStack>
+
+#include <QtXml/QXmlDefaultHandler>
+
+namespace TextEditor {
+namespace Internal {
+
+class KeywordList;
+class Context;
+class Rule;
+class HighlightDefinition;
+
+class HighlightDefinitionHandler : public QXmlDefaultHandler
+{
+public:
+ HighlightDefinitionHandler(const QSharedPointer<HighlightDefinition> &definition);
+ ~HighlightDefinitionHandler();
+
+ bool startDocument();
+ bool endDocument();
+ bool startElement(const QString &namespaceURI, const QString &localName,
+ const QString &qName, const QXmlAttributes &atts);
+ bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName);
+ bool characters(const QString &ch);
+
+private:
+ void listElementStarted(const QXmlAttributes &atts);
+ void itemElementStarted();
+ void contextElementStarted(const QXmlAttributes &atts);
+ void itemDataElementStarted(const QXmlAttributes &atts) const;
+ void commentElementStarted(const QXmlAttributes &atts) const;
+ void keywordsElementStarted(const QXmlAttributes &atts) const;
+ void ruleElementStarted(const QXmlAttributes &atts, const QSharedPointer<Rule> &rule);
+
+ // Specific rules.
+ void detectCharStarted(const QXmlAttributes &atts);
+ void detect2CharsStarted(const QXmlAttributes &atts);
+ void anyCharStarted(const QXmlAttributes &atts);
+ void stringDetectedStarted(const QXmlAttributes &atts);
+ void regExprStarted(const QXmlAttributes &atts);
+ void keywordStarted(const QXmlAttributes &atts);
+ void intStarted(const QXmlAttributes &atts);
+ void floatStarted(const QXmlAttributes &atts);
+ void hlCOctStarted(const QXmlAttributes &atts);
+ void hlCHexStarted(const QXmlAttributes &atts);
+ void hlCStringCharStarted(const QXmlAttributes &atts);
+ void hlCCharStarted(const QXmlAttributes &atts);
+ void rangeDetectStarted(const QXmlAttributes &atts);
+ void lineContinue(const QXmlAttributes &atts);
+ void includeRulesStarted(const QXmlAttributes &atts);
+ void detectSpacesStarted(const QXmlAttributes &atts);
+ void detectIdentifier(const QXmlAttributes &atts);
+
+ void processIncludeRules() const;
+ void processIncludeRules(const QSharedPointer<Context> &context) const;
+
+ QSharedPointer<HighlightDefinition> m_definition;
+
+ bool m_processingKeyword;
+ QString m_currentKeyword;
+ QSharedPointer<KeywordList> m_currentList;
+ QSharedPointer<Context> m_currentContext;
+ QStack<QSharedPointer<Rule> > m_currentRule;
+
+ bool m_initialContext;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // HIGHLIGHTDEFINITIONHANDLER_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "highlighter.h"
+#include "highlightdefinition.h"
+#include "context.h"
+#include "rule.h"
+#include "itemdata.h"
+#include "highlighterexception.h"
+#include "progressdata.h"
+#include "reuse.h"
+#include "texteditorconstants.h"
+#include "fontsettings.h"
+
+#include <QtCore/QLatin1String>
+#include <QtCore/QLatin1Char>
+
+using namespace TextEditor;
+using namespace Internal;
+
+namespace {
+ static const QLatin1String kStay("#stay");
+ static const QLatin1String kPop("#pop");
+ static const QLatin1Char kBackSlash('\\');
+ static const QLatin1Char kHash('#');
+}
+
+Highlighter::Highlighter(const QSharedPointer<Context> &defaultContext,QTextDocument *parent) :
+ QSyntaxHighlighter(parent),
+ m_persistentStatesCounter(PersistentsStart),
+ m_dynamicContextsCounter(0),
+ m_isBroken(false),
+ m_defaultContext(defaultContext)
+{
+ m_persistentStates.insert(m_defaultContext->name(), Default);
+}
+
+Highlighter::~Highlighter()
+{}
+
+Highlighter::BlockData::BlockData()
+{}
+
+Highlighter::BlockData::~BlockData()
+{}
+
+void Highlighter::highlightBlock(const QString &text)
+{
+ if (m_isBroken)
+ return;
+
+ try {
+ setupDataForBlock(text);
+
+ handleContextChange(m_currentContext->lineBeginContext(), m_currentContext->definition());
+
+ ProgressData progress;
+ const int length = text.length();
+ while (progress.offset() < length) {
+
+ if (progress.offset() > 0 &&
+ progress.onlySpacesSoFar() &&
+ !text.at(progress.offset()).isSpace()) {
+ progress.setOnlySpacesSoFar(false);
+ }
+
+ iterateThroughRules(text, length, &progress, false, m_currentContext->rules());
+ }
+
+ handleContextChange(m_currentContext->lineEndContext(), m_currentContext->definition(),
+ false);
+ } catch (const HighlighterException &) {
+ m_isBroken = true;
+ return;
+ }
+
+ m_contexts.clear();
+ applyVisualWhitespaceFormat(text);
+}
+
+void Highlighter::setupDataForBlock(const QString &text)
+{
+ if (currentBlockState() == WillContinue)
+ analyseConsistencyOfWillContinueBlock(text);
+
+ if (previousBlockState() == Default || previousBlockState() == -1)
+ setupDefault();
+ else if (previousBlockState() == WillContinue)
+ setupFromWillContinue();
+ else if (previousBlockState() == Continued)
+ setupFromContinued();
+ else
+ setupFromPersistent();
+
+ setCurrentContext();
+}
+
+void Highlighter::setupDefault()
+{
+ m_contexts.push_back(m_defaultContext);
+
+ setCurrentBlockState(Default);
+}
+
+void Highlighter::setupFromWillContinue()
+{
+ BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
+ m_contexts.push_back(previousData->m_contextToContinue);
+
+ if (!currentBlockUserData()) {
+ BlockData *data = initializeBlockData();
+ data->m_originalState = previousData->m_originalState;
+ }
+
+ if (currentBlockState() == Default || currentBlockState() == -1)
+ setCurrentBlockState(Continued);
+}
+
+void Highlighter::setupFromContinued()
+{
+ BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
+
+ Q_ASSERT(previousData->m_originalState != WillContinue &&
+ previousData->m_originalState != Continued);
+
+ if (previousData->m_originalState == Default || previousData->m_originalState == -1)
+ m_contexts.push_back(m_defaultContext);
+ else
+ pushContextSequence(previousData->m_originalState);
+
+ setCurrentBlockState(previousData->m_originalState);
+}
+
+void Highlighter::setupFromPersistent()
+{
+ pushContextSequence(previousBlockState());
+
+ setCurrentBlockState(previousBlockState());
+}
+
+void Highlighter::iterateThroughRules(const QString &text,
+ const int length,
+ ProgressData *progress,
+ const bool childRule,
+ const QList<QSharedPointer<Rule> > &rules)
+{
+ typedef QList<QSharedPointer<Rule> >::const_iterator RuleIterator;
+
+ bool contextChanged = false;
+ bool atLeastOneMatch = false;
+
+ RuleIterator it = rules.begin();
+ RuleIterator endIt = rules.end();
+ while (it != endIt && progress->offset() < length) {
+ int startOffset = progress->offset();
+
+ const QSharedPointer<Rule> &rule = *it;
+ if (rule->matchSucceed(text, length, progress)) {
+ atLeastOneMatch = true;
+
+ if (progress->willContinueLine()) {
+ createWillContinueBlock();
+ progress->setWillContinueLine(false);
+ progress->setOffset(length);
+ } else {
+ if (rule->hasChild())
+ iterateThroughRules(text, length, progress, true, rule->childs());
+
+ if (!rule->context().isEmpty() && contextChangeRequired(rule->context())) {
+ m_currentCaptures = progress->captures();
+ changeContext(rule->context(), rule->definition());
+ contextChanged = true;
+ }
+ }
+
+ // Format is not applied to child rules directly (but relative to the offset of their
+ // parent) nor to look ahead rules.
+ if (!childRule && !rule->isLookAhead()) {
+ if (rule->itemData().isEmpty())
+ applyFormat(startOffset, progress->offset() - startOffset,
+ m_currentContext->itemData(), m_currentContext->definition());
+ else
+ applyFormat(startOffset, progress->offset() - startOffset, rule->itemData(),
+ rule->definition());
+ }
+
+ // When there is a match of one child rule the others should be skipped. Otherwise
+ // the highlighting would be incorret in a case like 9ULLLULLLUULLULLUL, for example.
+ if (contextChanged || childRule) {
+ break;
+ } else {
+ it = rules.begin();
+ continue;
+ }
+ }
+ ++it;
+ }
+
+ if (!childRule && !atLeastOneMatch) {
+ if (m_currentContext->isFallthrough()) {
+ handleContextChange(m_currentContext->fallthroughContext(),
+ m_currentContext->definition());
+ iterateThroughRules(text, length, progress, false, m_currentContext->rules());
+ } else {
+ applyFormat(progress->offset(), 1, m_currentContext->itemData(),
+ m_currentContext->definition());
+ progress->incrementOffset();
+ }
+ }
+}
+
+bool Highlighter::contextChangeRequired(const QString &contextName) const
+{
+ if (contextName == kStay)
+ return false;
+ return true;
+}
+
+void Highlighter::changeContext(const QString &contextName,
+ const QSharedPointer<HighlightDefinition> &definition,
+ const bool setCurrent)
+{
+ if (contextName.startsWith(kPop)) {
+ QStringList list = contextName.split(kHash, QString::SkipEmptyParts);
+ for (int i = 0; i < list.size(); ++i)
+ m_contexts.pop_back();
+
+ if (currentBlockState() >= PersistentsStart) {
+ // One or more persistent contexts were popped.
+ const QString ¤tSequence = currentContextSequence();
+ if (m_persistentStates.contains(currentSequence))
+ setCurrentBlockState(m_persistentStates.value(currentContextSequence()));
+ else
+ setCurrentBlockState(m_leadingStates.value(currentContextSequence()));
+ }
+ } else {
+ const QSharedPointer<Context> &context = definition->context(contextName);
+
+ if (context->isDynamic())
+ pushDynamicContext(context);
+ else
+ m_contexts.push_back(context);
+
+ if (m_contexts.back()->lineEndContext() == kStay) {
+ // A persistent context was pushed.
+ const QString ¤tSequence = currentContextSequence();
+ mapContextSequence(currentSequence);
+ setCurrentBlockState(m_persistentStates.value(currentSequence));
+ }
+ }
+
+ if (setCurrent)
+ setCurrentContext();
+}
+
+void Highlighter::handleContextChange(const QString &contextName,
+ const QSharedPointer<HighlightDefinition> &definition,
+ const bool setCurrent)
+{
+ if (!contextName.isEmpty() && contextChangeRequired(contextName))
+ changeContext(contextName, definition, setCurrent);
+}
+
+void Highlighter::applyFormat(int offset,
+ int count,
+ const QString &itemDataName,
+ const QSharedPointer<HighlightDefinition> &definition)
+{
+ if (count == 0)
+ return;
+
+ QSharedPointer<ItemData> itemData;
+ try {
+ itemData = definition->itemData(itemDataName);
+ } catch (const HighlighterException &) {
+ // There are some broken files. For instance, the Printf context in java.xml points to an
+ // inexistent Printf item data. These cases are considered to have normal text style.
+ return;
+ }
+
+ if (itemData->style() != ItemData::kDsNormal) {
+ QTextCharFormat format = m_genericFormats.value(itemData->style());
+
+ if (itemData->isCustomized()) {
+ // Please notice that the following are applied every time for item datas which have
+ // customizations. The configureFormats method could be used to provide a "one time"
+ // configuration, but it would probably require to traverse all item datas from all
+ // definitions available/loaded (either to set the values or for some "notifying"
+ // strategy). This is because the highlighter does not really know on which
+ // definition(s) it is working. Since not many item datas specify customizations I
+ // think this approach would fit better. If there are other ideas...
+ if (itemData->color().isValid())
+ format.setForeground(itemData->color());
+ if (itemData->isItalicSpecified())
+ format.setFontItalic(itemData->isItalic());
+ if (itemData->isBoldSpecified())
+ format.setFontWeight(toFontWeight(itemData->isBold()));
+ if (itemData->isUnderlinedSpecified())
+ format.setFontUnderline(itemData->isUnderlined());
+ if (itemData->isStrikedOutSpecified())
+ format.setFontStrikeOut(itemData->isStrikedOut());
+ }
+
+ setFormat(offset, count, format);
+ }
+}
+
+void Highlighter::applyVisualWhitespaceFormat(const QString &text)
+{
+ int offset = 0;
+ const int length = text.length();
+ while (offset < length) {
+ if (text.at(offset).isSpace()) {
+ int start = offset++;
+ while (offset < length && text.at(offset).isSpace())
+ ++offset;
+ setFormat(start, offset - start, m_visualWhitespaceFormat);
+ } else {
+ ++offset;
+ }
+ }
+}
+
+void Highlighter::createWillContinueBlock()
+{
+ if (!currentBlockUserData())
+ initializeBlockData();
+
+ BlockData *data = static_cast<BlockData *>(currentBlockUserData());
+ if (currentBlockState() == Continued) {
+ BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
+ data->m_originalState = previousData->m_originalState;
+ } else if (currentBlockState() != WillContinue) {
+ data->m_originalState = currentBlockState();
+ }
+ data->m_contextToContinue = m_currentContext;
+
+ setCurrentBlockState(WillContinue);
+}
+
+void Highlighter::analyseConsistencyOfWillContinueBlock(const QString &text)
+{
+ if (currentBlock().next().isValid() && (
+ text.length() == 0 || text.at(text.length() - 1) != kBackSlash) &&
+ currentBlock().next().userState() != Continued) {
+ currentBlock().next().setUserState(Continued);
+ }
+
+ if (text.length() == 0 || text.at(text.length() - 1) != kBackSlash) {
+ BlockData *data = static_cast<BlockData *>(currentBlockUserData());
+ data->m_contextToContinue.clear();
+ setCurrentBlockState(data->m_originalState);
+ }
+}
+
+void Highlighter::mapContextSequence(const QString &contextSequence)
+{
+ if (currentBlockState() < PersistentsStart && !m_leadingStates.contains(contextSequence))
+ m_leadingStates.insert(contextSequence, currentBlockState());
+
+ if (!m_persistentStates.contains(contextSequence)) {
+ int newState = m_persistentStatesCounter;
+ m_persistentStates.insert(contextSequence, newState);
+ m_persistentContexts.insert(newState, m_contexts);
+ ++m_persistentStatesCounter;
+ }
+}
+
+void Highlighter::pushContextSequence(int state)
+{
+ const QVector<QSharedPointer<Context> > &contexts = m_persistentContexts.value(state);
+ for (int i = 0; i < contexts.size(); ++i)
+ m_contexts.push_back(contexts.at(i));
+}
+
+QString Highlighter::currentContextSequence() const
+{
+ QString sequence;
+ for (int i = 0; i < m_contexts.size(); ++i)
+ sequence.append(m_contexts.at(i)->id());
+
+ return sequence;
+}
+
+Highlighter::BlockData *Highlighter::initializeBlockData()
+{
+ BlockData *data = new BlockData;
+ setCurrentBlockUserData(data);
+ return data;
+}
+
+void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext)
+{
+ // A dynamic context is created from another context which serves as its basis. Then,
+ // its rules are updated according to the captures from the calling regular expression which
+ // triggered the push of the dynamic context.
+ QSharedPointer<Context> context(new Context(*baseContext));
+ context->configureId(m_dynamicContextsCounter);
+ context->updateDynamicRules(m_currentCaptures);
+ m_contexts.push_back(context);
+ ++m_dynamicContextsCounter;
+}
+
+void Highlighter::setCurrentContext()
+{
+ if (m_contexts.isEmpty()) {
+ // This is not supposed to happen. However, there are broken files (for example, php.xml)
+ // which will cause this behaviour. In such cases pushing the default context is enough to
+ // keep highlighter working.
+ m_contexts.push_back(m_defaultContext);
+ }
+ m_currentContext = m_contexts.back();
+}
+
+void Highlighter::configureFormats(const FontSettings & fs)
+{
+ m_visualWhitespaceFormat = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_VISUAL_WHITESPACE));
+
+ m_genericFormats[ItemData::kDsKeyword] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_KEYWORD));
+ m_genericFormats[ItemData::kDsDataType] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_TYPE));
+ // Currenlty using C_NUMBER for all kinds of numbers.
+ m_genericFormats[ItemData::kDsDecVal] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_NUMBER));
+ m_genericFormats[ItemData::kDsBaseN] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_NUMBER));
+ m_genericFormats[ItemData::kDsFloat] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_NUMBER));
+ // Currently using C_STRING for strings and chars.
+ m_genericFormats[ItemData::kDsChar] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_STRING));
+ m_genericFormats[ItemData::kDsString] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_STRING));
+ m_genericFormats[ItemData::kDsComment] = fs.toTextCharFormat(
+ QLatin1String(TextEditor::Constants::C_COMMENT));
+
+ // Currently Creator does not have corresponding formats for the following items. We can
+ // implement them... Just for now I will leave hardcoded colors.
+ QTextCharFormat format;
+ format.setForeground(Qt::blue);
+ m_genericFormats[ItemData::kDsOthers] = format;
+ format.setForeground(Qt::red);
+ m_genericFormats[ItemData::kDsAlert] = format;
+ format.setForeground(Qt::darkBlue);
+ m_genericFormats[ItemData::kDsFunction] = format;
+ format.setForeground(Qt::darkGray);
+ m_genericFormats[ItemData::kDsRegionMarker] = format;
+ format.setForeground(Qt::darkRed);
+ m_genericFormats[ItemData::kDsError] = format;
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 HIGHLIGHTER_H
+#define HIGHLIGHTER_H
+
+#include "basetextdocumentlayout.h"
+
+#include <QtCore/QString>
+#include <QtCore/QVector>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QStringList>
+
+#include <QtGui/QSyntaxHighlighter>
+
+namespace TextEditor {
+class FontSettings;
+}
+
+namespace TextEditor {
+namespace Internal {
+
+class Rule;
+class Context;
+class HighlightDefinition;
+class ProgressData;
+
+class Highlighter : public QSyntaxHighlighter
+{
+public:
+ Highlighter(const QSharedPointer<Context> &defaultContext, QTextDocument *parent = 0);
+ virtual ~Highlighter();
+
+ void configureFormats(const FontSettings & fs);
+
+protected:
+ virtual void highlightBlock(const QString &text);
+
+private:
+
+ void setupDataForBlock(const QString &text);
+ void setupDefault();
+ void setupFromWillContinue();
+ void setupFromContinued();
+ void setupFromPersistent();
+
+ void iterateThroughRules(const QString &text,
+ const int length,
+ ProgressData *progress,
+ const bool childRule,
+ const QList<QSharedPointer<Rule> > &rules);
+
+ bool contextChangeRequired(const QString &contextName) const;
+ void handleContextChange(const QString &contextName,
+ const QSharedPointer<HighlightDefinition> &definition,
+ const bool setCurrent = true);
+ void changeContext(const QString &contextName,
+ const QSharedPointer<HighlightDefinition> &definition,
+ const bool setCurrent = true);
+
+ void applyFormat(int offset,
+ int count,
+ const QString &itemDataName,
+ const QSharedPointer<HighlightDefinition> &definition);
+ void applyVisualWhitespaceFormat(const QString &text);
+
+ QString currentContextSequence() const;
+ void mapContextSequence(const QString &contextSequence);
+ void pushContextSequence(int state);
+ void pushDynamicContext(const QSharedPointer<Context> &baseContext);
+
+ void setCurrentContext();
+
+ void createWillContinueBlock();
+ void analyseConsistencyOfWillContinueBlock(const QString &text);
+
+ struct BlockData : TextBlockUserData
+ {
+ BlockData();
+ virtual ~BlockData();
+
+ int m_originalState;
+ QSharedPointer<Context> m_contextToContinue;
+ };
+ BlockData *initializeBlockData();
+
+ // Block states
+ // - Default [0]: Nothing special.
+ // - WillContinue [1]: When there is match of the LineContinue rule (backslash as the last
+ // character).
+ // - Continued [2]: Blocks that happen after a WillContinue block and continued from their
+ // context until the next line end.
+ // - Persistent(s) [Anything >= 3]: Correspond to persistent contexts which last until a pop
+ // occurs due to a matching rule. Every sequence of persistent contexts seen so far is
+ // associated with a number (incremented by a unit each time).
+ enum BlockState {
+ Default = 0,
+ WillContinue,
+ Continued,
+ PersistentsStart
+ };
+ int m_persistentStatesCounter;
+ int m_dynamicContextsCounter;
+
+ bool m_isBroken;
+
+ QSharedPointer<Context> m_defaultContext;
+ QSharedPointer<Context> m_currentContext;
+ QVector<QSharedPointer<Context> > m_contexts;
+
+ // Mapping from context sequences to the persistent state they represent.
+ QHash<QString, int> m_persistentStates;
+ // Mapping from context sequences to the non-persistent state that led to them.
+ QHash<QString, int> m_leadingStates;
+ // Mapping from persistent states to context sequences (the actual "stack").
+ QHash<int, QVector<QSharedPointer<Context> > > m_persistentContexts;
+
+ // Captures used in dynamic rules.
+ QStringList m_currentCaptures;
+
+ QTextCharFormat m_visualWhitespaceFormat;
+ QHash<QString, QTextCharFormat> m_genericFormats;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // HIGHLIGHTER_H
--- /dev/null
+/**************************************************************************
+**
+** 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 HIGHLIGHTEREXCEPTION_H
+#define HIGHLIGHTEREXCEPTION_H
+
+namespace TextEditor {
+namespace Internal {
+
+class HighlighterException {};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // HIGHLIGHTEREXCEPTION_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "includerulesinstruction.h"
+#include "reuse.h"
+
+using namespace TextEditor;
+using namespace Internal;
+
+IncludeRulesInstruction::IncludeRulesInstruction(const QString &context,
+ int hint,
+ const QString &replaceItemData) :
+ m_sourceContext(context), m_indexHint(hint), m_replaceItemData(toBool(replaceItemData))
+{
+}
+
+const QString &IncludeRulesInstruction::sourceContext() const
+{ return m_sourceContext; }
+
+int IncludeRulesInstruction::indexHint() const
+{ return m_indexHint; }
+
+bool IncludeRulesInstruction::replaceItemData() const
+{ return m_replaceItemData; }
--- /dev/null
+/**************************************************************************
+**
+** 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 INCLUDERULESINSTRUCTION_H
+#define INCLUDERULESINSTRUCTION_H
+
+#include <QtCore/QString>
+
+namespace TextEditor {
+namespace Internal {
+
+class IncludeRulesInstruction
+{
+public:
+ IncludeRulesInstruction(const QString &context, int hint, const QString &replaceItemData);
+
+ const QString &sourceContext() const;
+ int indexHint() const;
+ bool replaceItemData() const;
+
+private:
+ QString m_sourceContext;
+ int m_indexHint;
+ bool m_replaceItemData;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // INCLUDERULESINSTRUCTION_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "itemdata.h"
+#include "reuse.h"
+
+using namespace TextEditor;
+using namespace Internal;
+
+const QLatin1String ItemData::kDsNormal("dsNormal");
+const QLatin1String ItemData::kDsKeyword("dsKeyword");
+const QLatin1String ItemData::kDsDataType("dsDataType");
+const QLatin1String ItemData::kDsDecVal("dsDecVal");
+const QLatin1String ItemData::kDsBaseN("dsBaseN");
+const QLatin1String ItemData::kDsFloat("dsFloat");
+const QLatin1String ItemData::kDsChar("dsChar");
+const QLatin1String ItemData::kDsString("dsString");
+const QLatin1String ItemData::kDsComment("dsComment");
+const QLatin1String ItemData::kDsOthers("dsOthers");
+const QLatin1String ItemData::kDsAlert("dsAlert");
+const QLatin1String ItemData::kDsFunction("dsFunction");
+const QLatin1String ItemData::kDsRegionMarker("dsRegionMarker");
+const QLatin1String ItemData::kDsError("dsError");
+
+ItemData::ItemData() :
+ m_italicSpecified(false),
+ m_boldSpecified(false),
+ m_underlinedSpecified(false),
+ m_strikedOutSpecified(false),
+ m_isCustomized(false)
+{}
+
+void ItemData::setStyle(const QString &style)
+{ m_style = style; }
+
+const QString &ItemData::style() const
+{ return m_style; }
+
+void ItemData::setColor(const QString &color)
+{
+ if (!color.isEmpty()) {
+ m_color.setNamedColor(color);
+ m_isCustomized = true;
+ }
+}
+
+const QColor &ItemData::color() const
+{ return m_color; }
+
+void ItemData::setSelectionColor(const QString &color)
+{
+ if (!color.isEmpty()) {
+ m_selectionColor.setNamedColor(color);
+ m_isCustomized = true;
+ }
+}
+
+const QColor &ItemData::selectionColor() const
+{ return m_selectionColor; }
+
+void ItemData::setItalic(const QString &italic)
+{
+ if (!italic.isEmpty()) {
+ m_italic = toBool(italic);
+ m_italicSpecified = true;
+ m_isCustomized = true;
+ }
+}
+
+bool ItemData::isItalic() const
+{ return m_italic; }
+
+bool ItemData::isItalicSpecified() const
+{ return m_italicSpecified; }
+
+void ItemData::setBold(const QString &bold)
+{
+ if (!bold.isEmpty()) {
+ m_bold = toBool(bold);
+ m_boldSpecified = true;
+ m_isCustomized = true;
+ }
+}
+
+bool ItemData::isBold() const
+{ return m_bold; }
+
+bool ItemData::isBoldSpecified() const
+{ return m_boldSpecified; }
+
+void ItemData::setUnderlined(const QString &underlined)
+{
+ if (!underlined.isEmpty()) {
+ m_underlined = toBool(underlined);
+ m_underlinedSpecified = true;
+ m_isCustomized = true;
+ }
+}
+
+bool ItemData::isUnderlined() const
+{ return m_underlined; }
+
+bool ItemData::isUnderlinedSpecified() const
+{ return m_underlinedSpecified; }
+
+void ItemData::setStrikedOut(const QString &striked)
+{
+ if (!striked.isEmpty()) {
+ m_strikedOut = toBool(striked);
+ m_strikedOutSpecified = true;
+ m_isCustomized = true;
+ }
+}
+
+bool ItemData::isStrikedOut() const
+{ return m_strikedOut; }
+
+bool ItemData::isStrikedOutSpecified() const
+{ return m_strikedOutSpecified; }
+
+bool ItemData::isCustomized() const
+{ return m_isCustomized; }
--- /dev/null
+/**************************************************************************
+**
+** 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 ITEMDATA_H
+#define ITEMDATA_H
+
+#include <QtCore/QString>
+#include <QtGui/QColor>
+
+namespace TextEditor {
+namespace Internal {
+
+class ItemData
+{
+public:
+ ItemData();
+
+ void setStyle(const QString &style);
+ const QString &style() const;
+
+ void setColor(const QString &color);
+ const QColor &color() const;
+
+ void setSelectionColor(const QString &color);
+ const QColor &selectionColor() const;
+
+ void setItalic(const QString &italic);
+ bool isItalic() const;
+ bool isItalicSpecified() const;
+
+ void setBold(const QString &bold);
+ bool isBold() const;
+ bool isBoldSpecified() const;
+
+ void setUnderlined(const QString &underlined);
+ bool isUnderlined() const;
+ bool isUnderlinedSpecified() const;
+
+ void setStrikedOut(const QString &striked);
+ bool isStrikedOut() const;
+ bool isStrikedOutSpecified() const;
+
+ bool isCustomized() const;
+
+ static const QLatin1String kDsNormal;
+ static const QLatin1String kDsKeyword;
+ static const QLatin1String kDsDataType;
+ static const QLatin1String kDsDecVal;
+ static const QLatin1String kDsBaseN;
+ static const QLatin1String kDsFloat;
+ static const QLatin1String kDsChar;
+ static const QLatin1String kDsString;
+ static const QLatin1String kDsComment;
+ static const QLatin1String kDsOthers;
+ static const QLatin1String kDsAlert;
+ static const QLatin1String kDsFunction;
+ static const QLatin1String kDsRegionMarker;
+ static const QLatin1String kDsError;
+
+private:
+ QString m_style;
+ QColor m_color;
+ QColor m_selectionColor;
+ bool m_italic;
+ bool m_italicSpecified;
+ bool m_bold;
+ bool m_boldSpecified;
+ bool m_underlined;
+ bool m_underlinedSpecified;
+ bool m_strikedOut;
+ bool m_strikedOutSpecified;
+ bool m_isCustomized;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // ITEMDATA_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "keywordlist.h"
+
+using namespace TextEditor;
+using namespace Internal;
+
+void KeywordList::addKeyword(const QString &keyword)
+{
+ if (keyword.isEmpty())
+ return;
+
+ m_keywords.insert(keyword);
+}
+
+bool KeywordList::isKeyword(const QString &keyword, Qt::CaseSensitivity sensitivity) const
+{
+ if (keyword.isEmpty())
+ return false;
+
+ // Case sensitivity could be implemented, for example, by converting all keywords to lower
+ // if the global sensitivity attribute is insensitive, then always checking for containment
+ // (with a conversion to lower in the necessary cases). But the code below is one alternative
+ // to support the existence of local sensitivity attributes (which override the global one -
+ // currently not documented).
+ if (sensitivity == Qt::CaseSensitive) {
+ return m_keywords.contains(keyword);
+ } else {
+ foreach (const QString &s, m_keywords)
+ if (keyword.compare(s, Qt::CaseInsensitive) == 0)
+ return true;
+ return false;
+ }
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 KEYWORDLIST_H
+#define KEYWORDLIST_H
+
+#include <QtCore/QString>
+#include <QtCore/QSet>
+
+namespace TextEditor {
+namespace Internal {
+
+class KeywordList
+{
+public:
+
+ void addKeyword(const QString &keyword);
+ bool isKeyword(const QString &keyword, Qt::CaseSensitivity sensitivity) const;
+
+private:
+ QSet<QString> m_keywords;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // KEYWORDLIST_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "manager.h"
+#include "highlightdefinition.h"
+#include "highlightdefinitionhandler.h"
+#include "highlighterexception.h"
+#include "texteditorplugin.h"
+#include "texteditorsettings.h"
+#include "plaintexteditorfactory.h"
+
+#include <coreplugin/icore.h>
+#include <utils/qtcassert.h>
+#include <qtconcurrent/QtConcurrentTools>
+
+#include <QtCore/QtAlgorithms>
+#include <QtCore/QtPlugin>
+#include <QtCore/QString>
+#include <QtCore/QLatin1Char>
+#include <QtCore/QLatin1String>
+#include <QtCore/QStringList>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QDir>
+#include <QtCore/QList>
+#include <QtCore/QRegExp>
+#include <QtCore/QFuture>
+#include <QtCore/QtConcurrentRun>
+#include <QtXml/QXmlSimpleReader>
+#include <QtXml/QXmlInputSource>
+#include <QtXml/QXmlStreamReader>
+#include <QtXml/QXmlStreamAttributes>
+
+using namespace TextEditor;
+using namespace Internal;
+
+Manager::Manager()
+{}
+
+Manager::~Manager()
+{
+}
+
+Manager *Manager::instance()
+{
+ static Manager manager;
+ return &manager;
+}
+
+QString Manager::definitionIdByName(const QString &name) const
+{ return m_idByName.value(name); }
+
+QString Manager::definitionIdByMimeType(const QString &mimeType) const
+{
+ Q_ASSERT(!mimeType.isEmpty());
+
+ if (m_idByMimeType.count(mimeType) <= 1) {
+ return m_idByMimeType.value(mimeType);
+ } else {
+ QStringList candidateIds;
+ QMultiHash<QString, QString>::const_iterator it = m_idByMimeType.find(mimeType);
+ QMultiHash<QString, QString>::const_iterator endIt = m_idByMimeType.end();
+ for (; it != endIt && it.key() == mimeType; ++it)
+ candidateIds.append(it.value());
+
+ qSort(candidateIds.begin(), candidateIds.end(), m_priorityComp);
+ return candidateIds.last();
+ }
+}
+
+const QSharedPointer<HighlightDefinition> &Manager::definition(const QString &id)
+{
+ if (!m_definitions.contains(id)) {
+ m_isBuilding.insert(id);
+
+ QFile definitionFile(id);
+ if (!definitionFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ throw HighlighterException();
+ QXmlInputSource source(&definitionFile);
+
+ QSharedPointer<HighlightDefinition> definition(new HighlightDefinition);
+ HighlightDefinitionHandler handler(definition);
+
+ QXmlSimpleReader reader;
+ reader.setContentHandler(&handler);
+ reader.parse(source);
+
+ m_definitions.insert(id, definition);
+ definitionFile.close();
+ m_isBuilding.remove(id);
+ }
+
+ return *m_definitions.constFind(id);
+}
+
+bool Manager::isBuildingDefinition(const QString &id) const
+{ return m_isBuilding.contains(id); }
+
+void Manager::registerMimeTypes()
+{
+ QFuture<Core::MimeType> future =
+ QtConcurrent::run(&Manager::gatherDefinitionsMimeTypes, this);
+ m_watcher.setFuture(future);
+
+ connect(&m_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(registerMimeType(int)));
+}
+
+void Manager::gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &future)
+{
+ QDir definitionsDir(Core::ICore::instance()->resourcePath() +
+ QLatin1String("/generic-highlighter"));
+
+ QStringList filter(QLatin1String("*.xml"));
+ definitionsDir.setNameFilters(filter);
+
+ const QFileInfoList &filesInfo = definitionsDir.entryInfoList();
+ foreach (const QFileInfo &fileInfo, filesInfo) {
+ QString comment;
+ QStringList mimeTypes;
+ QStringList patterns;
+ parseDefinitionMetadata(fileInfo, &comment, &mimeTypes, &patterns);
+
+ // A definition can specify multiple MIME types and file extensions/patterns. However, each
+ // thing is done with a single string. Then, there is no direct way to tell which patterns
+ // belong to which MIME types nor whether a MIME type is just an alias for the other.
+ // Currently, I associate all expressions/patterns with all MIME types from a definition.
+
+ static const QStringList textPlain(QLatin1String("text/plain"));
+
+ QList<QRegExp> expressions;
+ foreach (const QString &type, mimeTypes) {
+ Core::MimeType mimeType = Core::ICore::instance()->mimeDatabase()->findByType(type);
+ if (mimeType.isNull()) {
+ if (expressions.isEmpty()) {
+ foreach (const QString &pattern, patterns)
+ expressions.append(QRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard));
+ }
+
+ mimeType.setType(type);
+ mimeType.setSubClassesOf(textPlain);
+ mimeType.setComment(comment);
+ mimeType.setGlobPatterns(expressions);
+
+ future.reportResult(mimeType);
+ }
+ }
+ }
+}
+
+void Manager::registerMimeType(int index) const
+{
+ const Core::MimeType &mimeType = m_watcher.resultAt(index);
+ Core::ICore::instance()->mimeDatabase()->addMimeType(mimeType);
+ TextEditorPlugin::instance()->editorFactory()->addMimeType(mimeType.type());
+}
+
+void Manager::parseDefinitionMetadata(const QFileInfo &fileInfo,
+ QString *comment,
+ QStringList *mimeTypes,
+ QStringList *patterns)
+{
+ static const QLatin1Char kSemiColon(';');
+ static const QLatin1Char kSlash('/');
+ static const QLatin1String kLanguage("language");
+ static const QLatin1String kName("name");
+ static const QLatin1String kExtensions("extensions");
+ static const QLatin1String kMimeType("mimetype");
+ static const QLatin1String kPriority("priority");
+ static const QLatin1String kArtificial("artificial");
+
+ const QString &id = fileInfo.absoluteFilePath();
+
+ QFile definitionFile(id);
+ if (!definitionFile.open(QIODevice::ReadOnly | QIODevice::Text))
+ return;
+
+ QXmlStreamReader reader(&definitionFile);
+ while (!reader.atEnd() && !reader.hasError()) {
+ if (reader.readNext() == QXmlStreamReader::StartElement &&
+ reader.name() == kLanguage) {
+ const QXmlStreamAttributes &attr = reader.attributes();
+
+ *comment = attr.value(kName).toString();
+ m_idByName.insert(*comment, id);
+
+ *patterns = attr.value(kExtensions).toString().split(kSemiColon,
+ QString::SkipEmptyParts);
+
+ *mimeTypes = attr.value(kMimeType).toString().split(kSemiColon,
+ QString::SkipEmptyParts);
+ if (mimeTypes->isEmpty()) {
+ // There are definitions which do not specify a MIME type, but specify file
+ // patterns. Creating an artificial MIME type is a workaround.
+ QString artificialType(kArtificial);
+ artificialType.append(kSlash).append(*comment);
+ m_idByMimeType.insert(artificialType, id);
+ mimeTypes->append(artificialType);
+ } else {
+ foreach (const QString &type, *mimeTypes)
+ m_idByMimeType.insert(type, id);
+ }
+
+ // The priority below should not be confused with the priority used when matching files
+ // to MIME types. Kate uses this when there are definitions which share equal
+ // extensions/patterns. Here it is for choosing a highlight definition if there are
+ // multiple ones associated with the same MIME type (should not happen in general).
+ m_priorityComp.m_priorityById.insert(id, attr.value(kPriority).toString().toInt());
+
+ break;
+ }
+ }
+ reader.clear();
+ definitionFile.close();
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 MANAGER_H
+#define MANAGER_H
+
+#include <coreplugin/mimedatabase.h>
+
+#include <QtCore/QString>
+#include <QtCore/QHash>
+#include <QtCore/QMultiHash>
+#include <QtCore/QSet>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QFutureWatcher>
+
+QT_BEGIN_NAMESPACE
+class QFileInfo;
+class QStringList;
+template <class> class QFutureInterface;
+QT_END_NAMESPACE
+
+namespace TextEditor {
+namespace Internal {
+
+class HighlightDefinition;
+
+class Manager : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~Manager();
+ static Manager *instance();
+
+ QString definitionIdByName(const QString &name) const;
+ QString definitionIdByMimeType(const QString &mimeType) const;
+ bool isBuildingDefinition(const QString &id) const;
+ const QSharedPointer<HighlightDefinition> &definition(const QString &id);
+
+private slots:
+ void registerMimeTypes();
+ void registerMimeType(int index) const;
+
+private:
+ Manager();
+ Q_DISABLE_COPY(Manager)
+
+ void gatherDefinitionsMimeTypes(QFutureInterface<Core::MimeType> &future);
+ void parseDefinitionMetadata(const QFileInfo &fileInfo,
+ QString *comment,
+ QStringList *mimeTypes,
+ QStringList *patterns);
+
+ struct PriorityCompare
+ {
+ bool operator()(const QString &a, const QString &b) const
+ { return m_priorityById.value(a) < m_priorityById.value(b); }
+
+ QHash<QString, int> m_priorityById;
+ };
+ PriorityCompare m_priorityComp;
+
+ QFutureWatcher<Core::MimeType> m_watcher;
+
+ QHash<QString, QString> m_idByName;
+ QMultiHash<QString, QString> m_idByMimeType;
+ QHash<QString, QSharedPointer<HighlightDefinition> > m_definitions;
+ QSet<QString> m_isBuilding;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // MANAGER_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "progressdata.h"
+
+#include <QtCore/QtGlobal>
+
+using namespace TextEditor;
+using namespace Internal;
+
+ProgressData::ProgressData() :
+ m_offset(0),
+ m_savedOffset(-1),
+ m_onlySpacesSoFar(true),
+ m_willContinueLine(false)
+{}
+
+void ProgressData::setOffset(const int offset)
+{ m_offset = offset; }
+
+int ProgressData::offset() const
+{ return m_offset; }
+
+void ProgressData::incrementOffset()
+{ ++m_offset; }
+
+void ProgressData::incrementOffset(const int increment)
+{ m_offset += increment; }
+
+void ProgressData::saveOffset()
+{ m_savedOffset = m_offset; }
+
+void ProgressData::restoreOffset()
+{
+ Q_ASSERT(m_savedOffset != -1);
+ m_offset = m_savedOffset;
+ m_savedOffset = -1;
+}
+
+void ProgressData::setOnlySpacesSoFar(const bool onlySpaces)
+{ m_onlySpacesSoFar = onlySpaces; }
+
+bool ProgressData::onlySpacesSoFar() const
+{ return m_onlySpacesSoFar; }
+
+void ProgressData::setWillContinueLine(const bool willContinue)
+{ m_willContinueLine = willContinue; }
+
+bool ProgressData::willContinueLine() const
+{ return m_willContinueLine; }
+
+void ProgressData::setCaptures(const QStringList &captures)
+{ m_captures = captures; }
+
+const QStringList &ProgressData::captures() const
+{ return m_captures; }
--- /dev/null
+/**************************************************************************
+**
+** 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 PROGRESSDATA_H
+#define PROGRESSDATA_H
+
+#include <QtCore/QStringList>
+
+namespace TextEditor {
+namespace Internal {
+
+class ProgressData
+{
+public:
+ ProgressData();
+
+ void setOffset(const int offset);
+ int offset() const;
+
+ void incrementOffset();
+ void incrementOffset(const int increment);
+
+ void saveOffset();
+ void restoreOffset();
+
+ void setOnlySpacesSoFar(const bool onlySpaces);
+ bool onlySpacesSoFar() const;
+
+ void setWillContinueLine(const bool willContinue);
+ bool willContinueLine() const;
+
+ void setCaptures(const QStringList &captures);
+ const QStringList &captures() const;
+
+private:
+ int m_offset;
+ int m_savedOffset;
+ bool m_onlySpacesSoFar;
+ bool m_willContinueLine;
+ QStringList m_captures;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // PROGRESSDATA_H
--- /dev/null
+/**************************************************************************
+**
+** 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 REUSE_H
+#define REUSE_H
+
+#include <Qt>
+#include <QtCore/QString>
+#include <QtCore/QLatin1String>
+#include <QtCore/QChar>
+#include <QtGui/QFont>
+
+namespace TextEditor {
+namespace Internal {
+
+inline bool toBool(const QString &s)
+{
+ static const QLatin1String kTrue("true");
+ static const QLatin1String k1("1");
+
+ if (s.toLower() == kTrue || s == k1)
+ return true;
+ return false;
+}
+
+
+inline Qt::CaseSensitivity toCaseSensitivity(const bool sensitive)
+{
+ if (sensitive)
+ return Qt::CaseSensitive;
+ return Qt::CaseInsensitive;
+}
+
+inline QFont::Weight toFontWeight(const bool bold)
+{
+ if (bold)
+ return QFont::Bold;
+ else
+ return QFont::Normal;
+}
+
+inline bool isOctalDigit(const QChar &c)
+{
+ static const QLatin1Char k0('0');
+ static const QLatin1Char k7('7');
+
+ return c >= k0 && c <= k7;
+}
+
+inline bool isHexDigit(const QChar &c)
+{
+ static const QLatin1Char k0('0');
+ static const QLatin1Char k9('9');
+ static const QLatin1Char kA('A');
+ static const QLatin1Char kF('F');
+ static const QLatin1Char ka('a');
+ static const QLatin1Char kf('f');
+
+ if ((c >= k0 && c <= k9) || (c >= kA && c <= kF) || (c >= ka && c <= kf))
+ return true;
+
+ return false;
+}
+
+inline void setStartCharacter(QChar &c, const QString &character)
+{
+ if (!character.isEmpty())
+ c = character.at(0);
+}
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // REUSE_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "rule.h"
+#include "highlighterexception.h"
+#include "progressdata.h"
+#include "highlightdefinition.h"
+#include "reuse.h"
+
+#include <QtCore/QStringList>
+
+#include <functional>
+
+using namespace TextEditor;
+using namespace Internal;
+
+const QLatin1Char Rule::kBackSlash('\\');
+const QLatin1Char Rule::kUnderscore('_');
+const QLatin1Char Rule::kDot('.');
+const QLatin1Char Rule::kPlus('+');
+const QLatin1Char Rule::kMinus('-');
+const QLatin1Char Rule::kZero('0');
+const QLatin1Char Rule::kQuote('\"');
+const QLatin1Char Rule::kSingleQuote('\'');
+const QLatin1Char Rule::kQuestion('?');
+const QLatin1Char Rule::kX('x');
+const QLatin1Char Rule::kA('a');
+const QLatin1Char Rule::kB('b');
+const QLatin1Char Rule::kE('e');
+const QLatin1Char Rule::kF('f');
+const QLatin1Char Rule::kN('n');
+const QLatin1Char Rule::kR('r');
+const QLatin1Char Rule::kT('t');
+const QLatin1Char Rule::kV('v');
+
+Rule::Rule(bool consumesNonSpace) :
+ m_lookAhead(false), m_firstNonSpace(false), m_column(-1), m_consumesNonSpace(consumesNonSpace)
+{}
+
+Rule::~Rule()
+{}
+
+void Rule::setContext(const QString &context)
+{ m_context = context; }
+
+const QString &Rule::context() const
+{ return m_context; }
+
+void Rule::setItemData(const QString &itemData)
+{ m_itemData = itemData; }
+
+const QString &Rule::itemData() const
+{ return m_itemData; }
+
+void Rule::setBeginRegion(const QString &begin)
+{ m_beginRegion = begin; }
+
+const QString &Rule::beginRegion() const
+{ return m_beginRegion; }
+
+void Rule::setEndRegion(const QString &end)
+{ m_endRegion = end; }
+
+const QString &Rule::endRegion() const
+{ return m_endRegion; }
+
+void Rule::setLookAhead(const QString &lookAhead)
+{ m_lookAhead = toBool(lookAhead); }
+
+bool Rule::isLookAhead() const
+{ return m_lookAhead; }
+
+void Rule::setFirstNonSpace(const QString &firstNonSpace)
+{ m_firstNonSpace = toBool(firstNonSpace); }
+
+bool Rule::isFirstNonSpace() const
+{ return m_firstNonSpace; }
+
+void Rule::setColumn(const QString &column)
+{
+ bool ok;
+ m_column = column.toInt(&ok);
+ if (!ok)
+ m_column = -1;
+}
+
+int Rule::column() const
+{ return m_column; }
+
+void Rule::addChild(const QSharedPointer<Rule> &rule)
+{ m_childRules.append(rule); }
+
+bool Rule::hasChild() const
+{ return !m_childRules.isEmpty(); }
+
+const QList<QSharedPointer<Rule> > &Rule::childs() const
+{ return m_childRules; }
+
+void Rule::setDefinition(const QSharedPointer<HighlightDefinition> &definition)
+{ m_definition = definition; }
+
+const QSharedPointer<HighlightDefinition> &Rule::definition() const
+{ return m_definition; }
+
+template <class predicate_t>
+bool Rule::predicateMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress,
+ const predicate_t &p) const
+{
+ int original = progress->offset();
+ while (progress->offset() < length && p(text.at(progress->offset())))
+ progress->incrementOffset();
+
+ if (original != progress->offset())
+ return true;
+
+ return false;
+}
+
+bool Rule::charPredicateMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool (QChar::* predicate)() const) const
+{
+ return predicateMatchSucceed(text, length, progress, std::mem_fun_ref(predicate));
+}
+
+bool Rule::charPredicateMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool (*predicate)(const QChar &)) const
+{
+ return predicateMatchSucceed(text, length, progress, std::ptr_fun(predicate));
+}
+
+bool Rule::matchSucceed(const QString &text, const int length, ProgressData *progress) const
+{
+ if (m_firstNonSpace && !progress->onlySpacesSoFar())
+ return false;
+
+ if (m_column != -1 && m_column != progress->offset())
+ return false;
+
+ int original = progress->offset();
+ if (doMatchSucceed(text, length, progress)) {
+ if (progress->onlySpacesSoFar() && !m_lookAhead && m_consumesNonSpace)
+ progress->setOnlySpacesSoFar(false);
+
+ if (m_lookAhead)
+ progress->setOffset(original);
+
+ return true;
+ }
+
+ return false;
+}
+
+Rule *Rule::clone() const
+{ return doClone(); }
+
+bool Rule::matchCharacter(const QString &text,
+ const int length,
+ ProgressData *progress,
+ const QChar &c,
+ bool saveRestoreOffset) const
+{
+ Q_UNUSED(length)
+ Q_ASSERT(progress->offset() < length);
+
+ if (text.at(progress->offset()) == c) {
+ if (saveRestoreOffset)
+ progress->saveOffset();
+ progress->incrementOffset();
+ return true;
+ }
+
+ return false;
+}
+
+bool Rule::matchEscapeSequence(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool saveRestoreOffset) const
+{
+ if (matchCharacter(text, length, progress, kBackSlash, saveRestoreOffset)) {
+
+ if (progress->offset() < length) {
+ const QChar &c = text.at(progress->offset());
+ if (c == kA || c == kB || c == kE || c == kF || c == kN || c == kR || c == kT ||
+ c == kV || c == kV || c == kQuestion || c == kSingleQuote || c == kQuote ||
+ c == kBackSlash) {
+ progress->incrementOffset();
+ return true;
+ } else if (saveRestoreOffset) {
+ progress->restoreOffset();
+ }
+ } else if (saveRestoreOffset) {
+ progress->restoreOffset();
+ }
+ }
+
+ return false;
+}
+
+bool Rule::matchOctalSequence(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool saveRestoreOffset) const
+{
+ // An octal sequence is identified as in the C++ Standard.
+ // octal-escape-sequence:
+ // \ octal-digit
+ // \ octal-digit octal-digit
+ // \ octal-digit octal-digit octal-digit
+
+ if (matchCharacter(text, length, progress, kBackSlash, saveRestoreOffset)) {
+
+ int count = 0;
+ while (progress->offset() < length &&
+ count < 3 &&
+ isOctalDigit(text.at(progress->offset()))) {
+ ++count;
+ progress->incrementOffset();
+ }
+
+ if (count > 0)
+ return true;
+ else if (saveRestoreOffset)
+ progress->restoreOffset();
+ }
+
+ return false;
+}
+
+bool Rule::matchHexSequence(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool saveRestoreOffset) const
+{
+ // An hex sequence is identified as in the C++ Standard.
+ // hexadecimal-escape-sequence:
+ // \x hexadecimal-digit
+ // hexadecimal-escape-sequence hexadecimal-digit
+
+ if (matchCharacter(text, length, progress, kBackSlash, saveRestoreOffset)) {
+
+ if (progress->offset() < length && matchCharacter(text, length, progress, kX, false)) {
+ bool found = false;
+ while (progress->offset() < length && isHexDigit(text.at(progress->offset()))) {
+ if (!found)
+ found = true;
+ progress->incrementOffset();
+ }
+
+ if (found)
+ return true;
+ else if (saveRestoreOffset)
+ progress->restoreOffset();
+ } else if (saveRestoreOffset) {
+ progress->restoreOffset();
+ }
+ }
+
+ return false;
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 RULE_H
+#define RULE_H
+
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtCore/QSharedPointer>
+
+namespace TextEditor {
+namespace Internal {
+
+class ProgressData;
+class HighlightDefinition;
+
+class Rule
+{
+public:
+ Rule(bool consumesNonSpace = true);
+ virtual ~Rule();
+
+ void setContext(const QString &context);
+ const QString &context() const;
+
+ void setItemData(const QString &itemData);
+ const QString &itemData() const;
+
+ void setBeginRegion(const QString &begin);
+ const QString &beginRegion() const;
+
+ void setEndRegion(const QString &end);
+ const QString &endRegion() const;
+
+ void setLookAhead(const QString &lookAhead);
+ bool isLookAhead() const;
+
+ void setFirstNonSpace(const QString &firstNonSpace);
+ bool isFirstNonSpace() const;
+
+ void setColumn(const QString &column);
+ int column() const;
+
+ void addChild(const QSharedPointer<Rule> &rule);
+ const QList<QSharedPointer<Rule> > &childs() const;
+ bool hasChild() const;
+
+ void setDefinition(const QSharedPointer<HighlightDefinition> &definition);
+ const QSharedPointer<HighlightDefinition> &definition() const;
+
+ bool matchSucceed(const QString &text, const int length, ProgressData *progress) const;
+
+ Rule *clone() const;
+
+protected:
+ bool charPredicateMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool (QChar::* predicate)() const) const;
+ bool charPredicateMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool (*predicate)(const QChar &)) const;
+
+ bool matchCharacter(const QString &text,
+ const int length,
+ ProgressData *progress,
+ const QChar &c,
+ bool saveRestoreOffset = true) const;
+ bool matchEscapeSequence(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool saveRestoreOffset = true) const;
+ bool matchOctalSequence(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool saveRestoreOffset = true) const;
+ bool matchHexSequence(const QString &text,
+ const int length,
+ ProgressData *progress,
+ bool saveRestoreOffset = true) const;
+
+ static const QLatin1Char kBackSlash;
+ static const QLatin1Char kUnderscore;
+ static const QLatin1Char kDot;
+ static const QLatin1Char kPlus;
+ static const QLatin1Char kMinus;
+ static const QLatin1Char kZero;
+ static const QLatin1Char kQuote;
+ static const QLatin1Char kSingleQuote;
+ static const QLatin1Char kQuestion;
+ static const QLatin1Char kX;
+ static const QLatin1Char kA;
+ static const QLatin1Char kB;
+ static const QLatin1Char kE;
+ static const QLatin1Char kF;
+ static const QLatin1Char kN;
+ static const QLatin1Char kR;
+ static const QLatin1Char kT;
+ static const QLatin1Char kV;
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const = 0;
+
+ virtual Rule *doClone() const = 0;
+
+ template <class predicate_t>
+ bool predicateMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress,
+ const predicate_t &p) const;
+
+ QString m_context;
+ QString m_itemData;
+ QString m_beginRegion;
+ QString m_endRegion;
+ bool m_lookAhead;
+ bool m_firstNonSpace;
+ int m_column;
+ bool m_consumesNonSpace;
+
+ QList<QSharedPointer<Rule> > m_childRules;
+
+ // Rules are represented within contexts. However, they have their own definition because
+ // of externally included rules.
+ QSharedPointer<HighlightDefinition> m_definition;
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // RULE_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "specificrules.h"
+#include "highlightdefinition.h"
+#include "keywordlist.h"
+#include "progressdata.h"
+#include "reuse.h"
+
+#include <QLatin1Char>
+
+using namespace TextEditor;
+using namespace Internal;
+
+namespace {
+
+void replaceByCaptures(QChar *c, const QStringList &captures)
+{
+ int index = c->digitValue();
+ if (index > 0) {
+ const QString &capture = captures.at(index);
+ *c = capture.at(0);
+ }
+}
+
+void replaceByCaptures(QString *s, const QStringList &captures)
+{
+ static const QLatin1Char kPercent('%');
+
+ int index;
+ int from = 0;
+ while ((index = s->indexOf(kPercent, from)) != -1) {
+ from = index + 1;
+
+ QString accumulator;
+ while (from < s->length() && s->at(from).isDigit()) {
+ accumulator.append(s->at(from));
+ ++from;
+ }
+
+ bool ok;
+ int number = accumulator.toInt(&ok);
+ Q_ASSERT(ok);
+
+ s->replace(index, accumulator.length() + 1, captures.at(number));
+ index = from;
+ }
+}
+}
+
+// DetectChar
+void DetectCharRule::setChar(const QString &character)
+{ setStartCharacter(m_char, character); }
+
+void DetectCharRule::doReplaceExpressions(const QStringList &captures)
+{ replaceByCaptures(&m_char, captures); }
+
+bool DetectCharRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ return matchCharacter(text, length, progress, m_char);
+}
+
+// Detect2Chars
+void Detect2CharsRule::setChar(const QString &character)
+{ setStartCharacter(m_char, character); }
+
+void Detect2CharsRule::setChar1(const QString &character)
+{ setStartCharacter(m_char1, character); }
+
+void Detect2CharsRule::doReplaceExpressions(const QStringList &captures)
+{
+ replaceByCaptures(&m_char, captures);
+ replaceByCaptures(&m_char1, captures);
+}
+
+bool Detect2CharsRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (matchCharacter(text, length, progress, m_char)) {
+ if (progress->offset() < length && matchCharacter(text, length, progress, m_char1, false))
+ return true;
+ else
+ progress->restoreOffset();
+ }
+
+ return false;
+}
+
+// AnyChar
+void AnyCharRule::setCharacterSet(const QString &s)
+{ m_characterSet = s; }
+
+bool AnyCharRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ Q_UNUSED(length)
+
+ if (m_characterSet.contains(text.at(progress->offset()))) {
+ progress->incrementOffset();
+ return true;
+ }
+
+ return false;
+}
+
+// StringDetect
+void StringDetectRule::setString(const QString &s)
+{
+ m_string = s;
+ m_length = m_string.length();
+}
+
+void StringDetectRule::setInsensitive(const QString &insensitive)
+{ m_caseSensitivity = toCaseSensitivity(!toBool(insensitive)); }
+
+void StringDetectRule::doReplaceExpressions(const QStringList &captures)
+{ replaceByCaptures(&m_string, captures); }
+
+bool StringDetectRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (length - progress->offset() >= m_length) {
+ QString candidate = text.fromRawData(text.unicode() + progress->offset(), m_length);
+ if (candidate.compare(m_string, m_caseSensitivity) == 0) {
+ progress->incrementOffset(m_length);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// RegExpr
+void RegExprRule::setPattern(const QString &pattern)
+{ m_expression.setPattern(pattern); }
+
+void RegExprRule::setInsensitive(const QString &insensitive)
+{ m_expression.setCaseSensitivity(toCaseSensitivity(!toBool(insensitive))); }
+
+void RegExprRule::setMinimal(const QString &minimal)
+{ m_expression.setMinimal(toBool(minimal)); }
+
+void RegExprRule::doReplaceExpressions(const QStringList &captures)
+{
+ QString s = m_expression.pattern();
+ replaceByCaptures(&s, captures);
+ m_expression.setPattern(s);
+}
+
+bool RegExprRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ Q_UNUSED(length)
+
+ // This is not documented but a regular expression match is considered valid if it starts
+ // at the current position and if the match length is not zero. Checked in Kate's source code
+ // after some unexpected problems.
+ const int offset = progress->offset();
+ if (m_expression.indexIn(text, offset, QRegExp::CaretAtZero) == offset) {
+ if (m_expression.matchedLength() == 0)
+ return false;
+ progress->incrementOffset(m_expression.matchedLength());
+ progress->setCaptures(m_expression.capturedTexts());
+ return true;
+ }
+
+ return false;
+}
+
+// Keyword
+KeywordRule::KeywordRule(const QSharedPointer<HighlightDefinition> &definition) :
+ m_overrideGlobal(false)
+{
+ setDefinition(definition);
+}
+
+KeywordRule::~KeywordRule()
+{}
+
+void KeywordRule::setInsensitive(const QString &insensitive)
+{
+ if (!insensitive.isEmpty()) {
+ m_overrideGlobal = true;
+ m_localCaseSensitivity = toCaseSensitivity(!toBool(insensitive));
+ }
+}
+
+void KeywordRule::setList(const QString &listName)
+{ m_list = definition()->keywordList(listName); }
+
+bool KeywordRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ int current = progress->offset();
+
+ if (current > 0 && !definition()->isDelimiter(text.at(current - 1)))
+ return false;
+ if (definition()->isDelimiter(text.at(current)))
+ return false;
+
+ while (current < length && !definition()->isDelimiter(text.at(current)))
+ ++current;
+
+ QString candidate =
+ QString::fromRawData(text.unicode() + progress->offset(), current - progress->offset());
+ if ((m_overrideGlobal && m_list->isKeyword(candidate, m_localCaseSensitivity)) ||
+ (!m_overrideGlobal && m_list->isKeyword(candidate, definition()->keywordsSensitive()))) {
+ progress->setOffset(current);
+ return true;
+ }
+
+ return false;
+}
+
+// Int
+bool IntRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ const int offset = progress->offset();
+
+ // This is necessary to correctly highlight an invalid octal like 09, for example.
+ if (offset > 0 && text.at(offset - 1).isDigit())
+ return false;
+
+ if (text.at(offset).isDigit() && text.at(offset) != kZero) {
+ progress->incrementOffset();
+ charPredicateMatchSucceed(text, length, progress, &QChar::isDigit);
+ return true;
+ }
+
+ return false;
+}
+
+// Float
+bool FloatRule::doMatchSucceed(const QString &text, const int length, ProgressData *progress) const
+{
+ progress->saveOffset();
+
+ bool integralPart = charPredicateMatchSucceed(text, length, progress, &QChar::isDigit);
+
+ bool decimalPoint = false;
+ if (progress->offset() < length && text.at(progress->offset()) == kDot) {
+ progress->incrementOffset();
+ decimalPoint = true;
+ }
+
+ bool fractionalPart = charPredicateMatchSucceed(text, length, progress, &QChar::isDigit);
+
+ bool exponentialPart = false;
+ int offset = progress->offset();
+ if (offset < length && (text.at(offset) == kE || text.at(offset).toLower() == kE)) {
+ progress->incrementOffset();
+
+ offset = progress->offset();
+ if (offset < length && (text.at(offset) == kPlus || text.at(offset) == kMinus))
+ progress->incrementOffset();
+
+ if (charPredicateMatchSucceed(text, length, progress, &QChar::isDigit)) {
+ exponentialPart = true;
+ } else {
+ progress->restoreOffset();
+ return false;
+ }
+ }
+
+ if ((integralPart || fractionalPart) && (decimalPoint || exponentialPart))
+ return true;
+
+ progress->restoreOffset();
+ return false;
+}
+
+// COctal
+bool HlCOctRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (matchCharacter(text, length, progress, kZero)) {
+ // In the definition files the number matching rules which are more restrictive should
+ // appear before the rules which are least resctritive. Although this happens in general
+ // there is at least one case where this is not strictly followed for existent definition
+ // files (specifically, HlCHex comes before HlCOct). So the condition below.
+ const int offset = progress->offset();
+ if (offset < length && (text.at(offset) == kX || text.at(offset).toLower() == kX)) {
+ progress->restoreOffset();
+ return false;
+ }
+
+ charPredicateMatchSucceed(text, length, progress, &isOctalDigit);
+ return true;
+ }
+
+ return false;
+}
+
+// CHex
+bool HlCHexRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (matchCharacter(text, length, progress, kZero)) {
+ const int offset = progress->offset();
+ if (offset < length && text.at(offset) != kX && text.at(offset).toLower() != kX) {
+ progress->restoreOffset();
+ return false;
+ }
+
+ progress->incrementOffset();
+ if (charPredicateMatchSucceed(text, length, progress, &isHexDigit))
+ return true;
+ else
+ progress->restoreOffset();
+ }
+
+ return false;
+}
+
+// CString
+bool HlCStringCharRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (matchEscapeSequence(text, length, progress))
+ return true;
+
+ if (matchOctalSequence(text, length, progress))
+ return true;
+
+ if (matchHexSequence(text, length, progress))
+ return true;
+
+ return false;
+}
+
+// CChar
+bool HlCCharRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (matchCharacter(text, length, progress, kSingleQuote)) {
+ if (progress->offset() < length) {
+ if (text.at(progress->offset()) != kBackSlash &&
+ text.at(progress->offset()) != kSingleQuote) {
+ progress->incrementOffset();
+ } else if (!matchEscapeSequence(text, length, progress, false)) {
+ progress->restoreOffset();
+ return false;
+ }
+
+ if (progress->offset() < length &&
+ matchCharacter(text, length, progress, kSingleQuote, false)) {
+ return true;
+ } else {
+ progress->restoreOffset();
+ }
+ } else {
+ progress->restoreOffset();
+ }
+ }
+
+ return false;
+}
+
+// RangeDetect
+void RangeDetectRule::setChar(const QString &character)
+{ setStartCharacter(m_char, character); }
+
+void RangeDetectRule::setChar1(const QString &character)
+{ setStartCharacter(m_char1, character); }
+
+bool RangeDetectRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (matchCharacter(text, length, progress, m_char)) {
+ while (progress->offset() < length) {
+ if (matchCharacter(text, length, progress, m_char1, false))
+ return true;
+ progress->incrementOffset();
+ }
+ progress->restoreOffset();
+ }
+
+ return false;
+}
+
+// LineContinue
+bool LineContinueRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ if (progress->offset() != length - 1)
+ return false;
+
+ if (text.at(progress->offset()) == kBackSlash) {
+ progress->setWillContinueLine(true);
+ return true;
+ }
+
+ return false;
+}
+
+// DetectSpaces
+DetectSpacesRule::DetectSpacesRule() : Rule(false)
+{}
+
+bool DetectSpacesRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ return charPredicateMatchSucceed(text, length, progress, &QChar::isSpace);
+}
+
+// DetectIdentifier
+bool DetectIdentifierRule::doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const
+{
+ // Identifiers are characterized by a letter or underscore as the first character and then
+ // zero or more word characters (\w*).
+ if (text.at(progress->offset()).isLetter() || text.at(progress->offset()) == kUnderscore) {
+ progress->incrementOffset();
+ while (progress->offset() < length) {
+ const QChar ¤t = text.at(progress->offset());
+ if (current.isLetterOrNumber() || current.isMark() || current == kUnderscore)
+ progress->incrementOffset();
+ else
+ break;
+ }
+ return true;
+ }
+ return false;
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 SPECIFICRULES_H
+#define SPECIFICRULES_H
+
+#include "rule.h"
+#include "dynamicrule.h"
+
+#include <QtCore/QChar>
+#include <QtCore/QString>
+#include <QtCore/QRegExp>
+#include <QtCore/QSharedPointer>
+
+namespace TextEditor {
+namespace Internal {
+
+class KeywordList;
+class HighlightDefinition;
+
+class DetectCharRule : public DynamicRule
+{
+public:
+ virtual ~DetectCharRule() {}
+
+ void setChar(const QString &character);
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual DetectCharRule *doClone() const { return new DetectCharRule(*this); }
+ virtual void doReplaceExpressions(const QStringList &captures);
+
+ QChar m_char;
+};
+
+class Detect2CharsRule : public DynamicRule
+{
+public:
+ virtual ~Detect2CharsRule() {}
+
+ void setChar(const QString &character);
+ void setChar1(const QString &character);
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual Detect2CharsRule *doClone() const { return new Detect2CharsRule(*this); }
+ virtual void doReplaceExpressions(const QStringList &captures);
+
+ QChar m_char;
+ QChar m_char1;
+};
+
+class AnyCharRule : public Rule
+{
+public:
+ virtual ~AnyCharRule() {}
+
+ void setCharacterSet(const QString &s);
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual AnyCharRule *doClone() const { return new AnyCharRule(*this); }
+
+ QString m_characterSet;
+};
+
+class StringDetectRule : public DynamicRule
+{
+public:
+ virtual ~StringDetectRule() {}
+
+ void setString(const QString &s);
+ void setInsensitive(const QString &insensitive);
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual StringDetectRule *doClone() const { return new StringDetectRule(*this); }
+ virtual void doReplaceExpressions(const QStringList &captures);
+
+ QString m_string;
+ int m_length;
+ Qt::CaseSensitivity m_caseSensitivity;
+};
+
+class RegExprRule : public DynamicRule
+{
+public:
+ virtual ~RegExprRule() {}
+
+ void setPattern(const QString &pattern);
+ void setInsensitive(const QString &insensitive);
+ void setMinimal(const QString &minimal);
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual RegExprRule *doClone() const { return new RegExprRule(*this); }
+ virtual void doReplaceExpressions(const QStringList &captures);
+
+ QRegExp m_expression;
+};
+
+class KeywordRule : public Rule
+{
+public:
+ KeywordRule(const QSharedPointer<HighlightDefinition> &definition);
+ virtual ~KeywordRule();
+
+ void setInsensitive(const QString &insensitive);
+ void setList(const QString &listName);
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual KeywordRule *doClone() const { return new KeywordRule(*this); }
+
+ bool m_overrideGlobal;
+ Qt::CaseSensitivity m_localCaseSensitivity;
+ QSharedPointer<KeywordList> m_list;
+};
+
+class IntRule : public Rule
+{
+public:
+ virtual ~IntRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual IntRule *doClone() const { return new IntRule(*this); }
+};
+
+class FloatRule : public Rule
+{
+public:
+ virtual ~FloatRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual FloatRule *doClone() const { return new FloatRule(*this); }
+};
+
+class HlCOctRule : public Rule
+{
+public:
+ virtual ~HlCOctRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual HlCOctRule *doClone() const { return new HlCOctRule(*this); }
+};
+
+class HlCHexRule : public Rule
+{
+public:
+ virtual ~HlCHexRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual HlCHexRule *doClone() const { return new HlCHexRule(*this); }
+};
+
+class HlCStringCharRule : public Rule
+{
+public:
+ virtual ~HlCStringCharRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual HlCStringCharRule *doClone() const { return new HlCStringCharRule(*this); }
+};
+
+class HlCCharRule : public Rule
+{
+public:
+ virtual ~HlCCharRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual HlCCharRule *doClone() const { return new HlCCharRule(*this); }
+};
+
+class RangeDetectRule : public Rule
+{
+public:
+ virtual ~RangeDetectRule() {}
+
+ void setChar(const QString &character);
+ void setChar1(const QString &character);
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual RangeDetectRule *doClone() const { return new RangeDetectRule(*this); }
+
+ QChar m_char;
+ QChar m_char1;
+};
+
+class LineContinueRule : public Rule
+{
+public:
+ virtual ~LineContinueRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual LineContinueRule *doClone() const { return new LineContinueRule(*this); }
+};
+
+class DetectSpacesRule : public Rule
+{
+public:
+ DetectSpacesRule();
+ virtual ~DetectSpacesRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual DetectSpacesRule *doClone() const { return new DetectSpacesRule(*this); }
+};
+
+class DetectIdentifierRule : public Rule
+{
+public:
+ virtual ~DetectIdentifierRule() {}
+
+private:
+ virtual bool doMatchSucceed(const QString &text,
+ const int length,
+ ProgressData *progress) const;
+ virtual DetectIdentifierRule *doClone() const { return new DetectIdentifierRule(*this); }
+};
+
+} // namespace Internal
+} // namespace TextEditor
+
+#endif // SPECIFICRULES_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "indenter.h"
+#include "tabsettings.h"
+
+using namespace TextEditor;
+
+Indenter::Indenter()
+{}
+
+Indenter::~Indenter()
+{}
+
+void Indenter::indentBlock(QTextDocument *doc,
+ QTextBlock block,
+ QChar typedChar,
+ const TabSettings &ts)
+{
+ doIndentBlock(doc, block, typedChar, ts);
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 INDENTER_H
+#define INDENTER_H
+
+#include "texteditor_global.h"
+
+#include <QtCore/QChar>
+#include <QtGui/QTextBlock>
+
+QT_BEGIN_NAMESPACE
+class QTextDocument;
+QT_END_NAMESPACE
+
+namespace TextEditor {
+
+struct TabSettings;
+
+class TEXTEDITOR_EXPORT Indenter
+{
+public:
+ Indenter();
+ virtual ~Indenter();
+
+ void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar, const TabSettings &ts);
+
+private:
+ virtual void doIndentBlock(QTextDocument *doc,
+ QTextBlock block,
+ QChar typedChar,
+ const TabSettings &ts) = 0;
+};
+
+} // namespace TextEditor
+
+#endif // INDENTER_H
--- /dev/null
+/**************************************************************************
+**
+** 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 "normalindenter.h"
+#include "tabsettings.h"
+
+#include <QtGui/QTextDocument>
+
+using namespace TextEditor;
+
+NormalIndenter::NormalIndenter()
+{}
+
+NormalIndenter::~NormalIndenter()
+{}
+
+// Indent a text block based on previous line.
+// Simple text paragraph layout:
+// aaaa aaaa
+//
+// bbb bb
+// bbb bb
+//
+// - list
+// list line2
+//
+// - listn
+//
+// ccc
+//
+// @todo{Add formatting to wrap paragraphs. This requires some
+// hoops as the current indentation routines are not prepared
+// for additional block being inserted. It might be possible
+// to do in 2 steps (indenting/wrapping)}
+//
+void NormalIndenter::doIndentBlock(QTextDocument *doc,
+ QTextBlock block,
+ QChar typedChar,
+ const TextEditor::TabSettings &ts)
+{
+ Q_UNUSED(typedChar)
+
+ // At beginning: Leave as is.
+ if (block == doc->begin())
+ return;
+
+ const QTextBlock previous = block.previous();
+ const QString previousText = previous.text();
+ // Empty line indicates a start of a new paragraph. Leave as is.
+ if (previousText.isEmpty() || previousText.trimmed().isEmpty())
+ return;
+
+ // Just use previous line.
+ // Skip blank characters when determining the indentation
+ int i = 0;
+ while (i < previousText.size()) {
+ if (!previousText.at(i).isSpace()) {
+ ts.indentLine(block, ts.columnAt(previousText, i));
+ break;
+ }
+ ++i;
+ }
+}
--- /dev/null
+/**************************************************************************
+**
+** 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 NORMALINDENTER_H
+#define NORMALINDENTER_H
+
+#include "indenter.h"
+
+namespace TextEditor {
+
+class TEXTEDITOR_EXPORT NormalIndenter : public Indenter
+{
+public:
+ NormalIndenter();
+ virtual ~NormalIndenter();
+
+private:
+ virtual void doIndentBlock(QTextDocument *doc,
+ QTextBlock block,
+ QChar typedChar,
+ const TabSettings &ts);
+};
+
+} // namespace TextEditor
+
+#endif // NORMALINDENTER_H
#include "tabsettings.h"
#include "texteditorconstants.h"
#include "texteditorplugin.h"
+#include "texteditorsettings.h"
+#include "basetextdocument.h"
+#include "highlightdefinition.h"
+#include "highlighter.h"
+#include "highlighterexception.h"
+#include "manager.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/uniqueidmanager.h>
+#include <coreplugin/icore.h>
+#include <coreplugin/mimedatabase.h>
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QFileInfo>
using namespace TextEditor;
using namespace TextEditor::Internal;
setRequestMarkEnabled(false);
setLineSeparatorsAllowed(true);
- setMimeType(QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT));
setDisplayName(tr(Core::Constants::K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME));
+
+ m_commentDefinition.clearCommentStyles();
+
+ connect(file(), SIGNAL(changed()), this, SLOT(configure()));
}
QList<int> PlainTextEditorEditable::context() const
return QLatin1String(Core::Constants::K_DEFAULT_TEXT_EDITOR_ID);
}
-// Indent a text block based on previous line.
-// Simple text paragraph layout:
-// aaaa aaaa
-//
-// bbb bb
-// bbb bb
-//
-// - list
-// list line2
-//
-// - listn
-//
-// ccc
-//
-// @todo{Add formatting to wrap paragraphs. This requires some
-// hoops as the current indentation routines are not prepared
-// for additional block being inserted. It might be possible
-// to do in 2 steps (indenting/wrapping)}
-//
+void PlainTextEditor::unCommentSelection()
+{
+ Utils::unCommentSelection(this, m_commentDefinition);
+}
-void PlainTextEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar)
+void PlainTextEditor::setFontSettings(const TextEditor::FontSettings & fs)
{
- Q_UNUSED(typedChar)
+ TextEditor::BaseTextEditor::setFontSettings(fs);
- // At beginning: Leave as is.
- if (block == doc->begin())
+ Highlighter *highlighter = static_cast<Highlighter *>(baseTextDocument()->syntaxHighlighter());
+ if (!highlighter)
return;
- const QTextBlock previous = block.previous();
- const QString previousText = previous.text();
- // Empty line indicates a start of a new paragraph. Leave as is.
- if (previousText.isEmpty() || previousText.trimmed().isEmpty())
- return;
+ highlighter->configureFormats(fs);
+ highlighter->rehighlight();
+}
+
+void PlainTextEditor::configure()
+{
+ const QString &mimeType = Core::ICore::instance()->mimeDatabase()->findByFile(
+ QFileInfo(file()->fileName())).type();
+ baseTextDocument()->setMimeType(mimeType);
+
+ const QString &definitionId = Manager::instance()->definitionIdByMimeType(mimeType);
+ if (!definitionId.isEmpty()) {
+ try {
+ const QSharedPointer<HighlightDefinition> &definition =
+ Manager::instance()->definition(definitionId);
+
+ Highlighter *highlighter = new Highlighter(definition->initialContext());
+ highlighter->configureFormats(TextEditor::TextEditorSettings::instance()->fontSettings());
- // Just use previous line.
- // Skip blank characters when determining the indentation
- int i = 0;
- while (i < previousText.size()) {
- if (!previousText.at(i).isSpace()) {
- const TextEditor::TabSettings &ts = tabSettings();
- ts.indentLine(block, ts.columnAt(previousText, i));
- break;
+ baseTextDocument()->setSyntaxHighlighter(highlighter);
+
+ m_commentDefinition.setAfterWhiteSpaces(definition->isCommentAfterWhiteSpaces());
+ m_commentDefinition.setSingleLine(definition->singleLineComment());
+ m_commentDefinition.setMultiLineStart(definition->multiLineCommentStart());
+ m_commentDefinition.setMultiLineEnd(definition->multiLineCommentEnd());
+ } catch (const HighlighterException &) {
}
- ++i;
}
+
+ // @todo: Indentation specification through the definition files is not really being
+ // used because Kate recommends to configure indentation through another feature.
+ // Maybe we should provide something similar in Creator? For now, only normal
+ // indentation is supported.
+ m_indenter.reset(new TextEditor::NormalIndenter);
+}
+
+void PlainTextEditor::indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar)
+{
+ m_indenter->indentBlock(doc, block, typedChar, tabSettings());
}
#define PLAINTEXTEDITOR_H
#include "basetexteditor.h"
+#include "normalindenter.h"
+
+#include <utils/uncommentselection.h>
#include <QtCore/QList>
+#include <QtCore/QScopedPointer>
namespace TextEditor {
public:
PlainTextEditor(QWidget *parent);
+public slots:
+ virtual void unCommentSelection();
+ virtual void setFontSettings(const TextEditor::FontSettings &);
+
+private slots:
+ void configure();
+
protected:
- virtual BaseTextEditorEditable *createEditableInterface() { return new PlainTextEditorEditable(this); }
- // Indent a text block based on previous line.
+ virtual BaseTextEditorEditable *createEditableInterface() { return new PlainTextEditorEditable(this); }
virtual void indentBlock(QTextDocument *doc, QTextBlock block, QChar typedChar);
+
+private:
+ Utils::CommentDefinition m_commentDefinition;
+ QScopedPointer<TextEditor::Indenter> m_indenter;
};
} // namespace TextEditor
{
m_actionHandler = new TextEditorActionHandler(
QLatin1String(TextEditor::Constants::C_TEXTEDITOR),
- TextEditorActionHandler::Format);
- m_mimeTypes << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT)
- << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_XML);
+ TextEditorActionHandler::Format |
+ TextEditorActionHandler::UnCommentSelection);
+ m_mimeTypes << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT);
}
PlainTextEditorFactory::~PlainTextEditorFactory()
return rc->editableInterface();
}
+void PlainTextEditorFactory::addMimeType(const QString &type)
+{
+ m_mimeTypes.append(type);
+}
+
QStringList PlainTextEditorFactory::mimeTypes() const
{
return m_mimeTypes;
PlainTextEditorFactory(QObject *parent = 0);
virtual ~PlainTextEditorFactory();
+ void addMimeType(const QString &type);
virtual QStringList mimeTypes() const;
//Core::IEditorFactory
QString id() const;
TEMPLATE = lib
TARGET = TextEditor
DEFINES += TEXTEDITOR_LIBRARY
+QT += xml
include(../../qtcreatorplugin.pri)
include(texteditor_dependencies.pri)
+INCLUDEPATH += generichighlighter
+DEPENDPATH += generichighlighter
SOURCES += texteditorplugin.cpp \
textfilewizard.cpp \
plaintexteditor.cpp \
texteditoroverlay.cpp \
texteditoroptionspage.cpp \
basetextdocumentlayout.cpp \
- completionsettings.cpp
+ completionsettings.cpp \
+ normalindenter.cpp \
+ indenter.cpp \
+ generichighlighter/itemdata.cpp \
+ generichighlighter/specificrules.cpp \
+ generichighlighter/rule.cpp \
+ generichighlighter/dynamicrule.cpp \
+ generichighlighter/context.cpp \
+ generichighlighter/includerulesinstruction.cpp \
+ generichighlighter/progressdata.cpp \
+ generichighlighter/keywordlist.cpp \
+ generichighlighter/highlightdefinition.cpp \
+ generichighlighter/highlighter.cpp \
+ generichighlighter/manager.cpp \
+ generichighlighter/highlightdefinitionhandler.cpp
HEADERS += texteditorplugin.h \
textfilewizard.h \
texteditoroverlay.h \
texteditoroptionspage.h \
basetextdocumentlayout.h \
- completionsettings.h
+ completionsettings.h \
+ normalindenter.h \
+ indenter.h \
+ generichighlighter/reuse.h \
+ generichighlighter/itemdata.h \
+ generichighlighter/specificrules.h \
+ generichighlighter/rule.h \
+ generichighlighter/reuse.h \
+ generichighlighter/dynamicrule.h \
+ generichighlighter/context.h \
+ generichighlighter/includerulesinstruction.h \
+ generichighlighter/progressdata.h \
+ generichighlighter/keywordlist.h \
+ generichighlighter/highlighterexception.h \
+ generichighlighter/highlightdefinition.h \
+ generichighlighter/highlighter.h \
+ generichighlighter/manager.h \
+ generichighlighter/highlightdefinitionhandler.h
FORMS += behaviorsettingspage.ui \
m_cleanWhitespaceAction(0),
m_textWrappingAction(0),
m_unCommentSelectionAction(0),
- m_unCollapseAllAction(0),
- m_collapseAction(0),
- m_expandAction(0),
+ m_unfoldAllAction(0),
+ m_foldAction(0),
+ m_unfoldAction(0),
m_cutLineAction(0),
m_deleteLineAction(0),
m_selectEncodingAction(0),
command = am->registerAction(m_deleteLineAction, Constants::DELETE_LINE, m_contextId);
connect(m_deleteLineAction, SIGNAL(triggered()), this, SLOT(deleteLine()));
- m_collapseAction = new QAction(tr("Collapse"), this);
- command = am->registerAction(m_collapseAction, Constants::COLLAPSE, m_contextId);
+ m_foldAction = new QAction(tr("Fold"), this);
+ command = am->registerAction(m_foldAction, Constants::FOLD, m_contextId);
command->setDefaultKeySequence(QKeySequence(tr("Ctrl+<")));
- connect(m_collapseAction, SIGNAL(triggered()), this, SLOT(collapse()));
+ connect(m_foldAction, SIGNAL(triggered()), this, SLOT(fold()));
advancedMenu->addAction(command, Core::Constants::G_EDIT_COLLAPSING);
- m_expandAction = new QAction(tr("Expand"), this);
- command = am->registerAction(m_expandAction, Constants::EXPAND, m_contextId);
+ m_unfoldAction = new QAction(tr("Unfold"), this);
+ command = am->registerAction(m_unfoldAction, Constants::UNFOLD, m_contextId);
command->setDefaultKeySequence(QKeySequence(tr("Ctrl+>")));
- connect(m_expandAction, SIGNAL(triggered()), this, SLOT(expand()));
+ connect(m_unfoldAction, SIGNAL(triggered()), this, SLOT(unfold()));
advancedMenu->addAction(command, Core::Constants::G_EDIT_COLLAPSING);
- m_unCollapseAllAction = new QAction(tr("(Un)&Collapse All"), this);
- command = am->registerAction(m_unCollapseAllAction, Constants::UN_COLLAPSE_ALL, m_contextId);
- connect(m_unCollapseAllAction, SIGNAL(triggered()), this, SLOT(unCollapseAll()));
+ m_unfoldAllAction = new QAction(tr("(Un)&Collapse All"), this);
+ command = am->registerAction(m_unfoldAllAction, Constants::UNFOLD_ALL, m_contextId);
+ connect(m_unfoldAllAction, SIGNAL(triggered()), this, SLOT(unfoldAll()));
advancedMenu->addAction(command, Core::Constants::G_EDIT_COLLAPSING);
m_increaseFontSizeAction = new QAction(tr("Increase Font Size"), this);
command->setDefaultKeySequence(QKeySequence(tr("Ctrl+J")));
connect(m_joinLinesAction, SIGNAL(triggered()), this, SLOT(joinLines()));
+ m_insertLineAboveAction = new QAction(tr("Insert Line Above Current Line"), this);
+ command = am->registerAction(m_insertLineAboveAction, Constants::INSERT_LINE_ABOVE, m_contextId);
+ command->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+Return")));
+ connect(m_insertLineAboveAction, SIGNAL(triggered()), this, SLOT(insertLineAbove()));
+
+ m_insertLineBelowAction = new QAction(tr("Insert Line Below Current Line"), this);
+ command = am->registerAction(m_insertLineBelowAction, Constants::INSERT_LINE_BELOW, m_contextId);
+ command->setDefaultKeySequence(QKeySequence(tr("Ctrl+Return")));
+ connect(m_insertLineBelowAction, SIGNAL(triggered()), this, SLOT(insertLineBelow()));
QAction *a = 0;
a = new QAction(tr("Goto Line Start"), this);
m_moveLineDownAction->setEnabled(um != ReadOnlyMode);
m_formatAction->setEnabled((m_optionalActions & Format));
- m_unCollapseAllAction->setEnabled((m_optionalActions & UnCollapseAll));
+ m_unfoldAllAction->setEnabled((m_optionalActions & UnCollapseAll));
m_visualizeWhitespaceAction->setChecked(m_currentEditor->displaySettings().m_visualizeWhitespace);
if (m_textWrappingAction) {
m_textWrappingAction->setChecked(m_currentEditor->displaySettings().m_textWrapping);
FUNCTION(unCommentSelection)
FUNCTION(cutLine)
FUNCTION(deleteLine)
-FUNCTION(unCollapseAll)
-FUNCTION(collapse)
-FUNCTION(expand)
+FUNCTION(unfoldAll)
+FUNCTION(fold)
+FUNCTION(unfold)
FUNCTION2(increaseFontSize, zoomIn)
FUNCTION2(decreaseFontSize, zoomOut)
FUNCTION2(resetFontSize, zoomReset)
FUNCTION(copyLineUp)
FUNCTION(copyLineDown)
FUNCTION(joinLines)
+FUNCTION(insertLineAbove)
+FUNCTION(insertLineBelow)
FUNCTION(gotoLineStart)
FUNCTION(gotoLineStartWithSelection)
void cleanWhitespace();
void setTextWrapping(bool);
void unCommentSelection();
- void unCollapseAll();
- void collapse();
- void expand();
+ void unfoldAll();
+ void fold();
+ void unfold();
void cutLine();
void deleteLine();
void selectEncoding();
void copyLineUp();
void copyLineDown();
void joinLines();
+ void insertLineAbove();
+ void insertLineBelow();
void updateCurrentEditor(Core::IEditor *editor);
void gotoLineStart();
QAction *m_cleanWhitespaceAction;
QAction *m_textWrappingAction;
QAction *m_unCommentSelectionAction;
- QAction *m_unCollapseAllAction;
- QAction *m_collapseAction;
- QAction *m_expandAction;
+ QAction *m_unfoldAllAction;
+ QAction *m_foldAction;
+ QAction *m_unfoldAction;
QAction *m_cutLineAction;
QAction *m_deleteLineAction;
QAction *m_selectEncodingAction;
QAction *m_copyLineUpAction;
QAction *m_copyLineDownAction;
QAction *m_joinLinesAction;
+ QAction *m_insertLineAboveAction;
+ QAction *m_insertLineBelowAction;
uint m_optionalActions;
QPointer<BaseTextEditor> m_currentEditor;
const char * const TEXT_WRAPPING = "TextEditor.TextWrapping";
const char * const UN_COMMENT_SELECTION = "TextEditor.UnCommentSelection";
const char * const REFORMAT = "TextEditor.Reformat";
-const char * const COLLAPSE = "TextEditor.Collapse";
-const char * const EXPAND = "TextEditor.Expand";
-const char * const UN_COLLAPSE_ALL = "TextEditor.UnCollapseAll";
+const char * const FOLD = "TextEditor.Fold";
+const char * const UNFOLD = "TextEditor.Unfold";
+const char * const UNFOLD_ALL = "TextEditor.UnCollapseAll";
const char * const AUTO_INDENT_SELECTION = "TextEditor.AutoIndentSelection";
const char * const INCREASE_FONT_SIZE = "TextEditor.IncreaseFontSize";
const char * const DECREASE_FONT_SIZE = "TextEditor.DecreaseFontSize";
const char * const COPY_LINE_UP = "TextEditor.CopyLineUp";
const char * const COPY_LINE_DOWN = "TextEditor.CopyLineDown";
const char * const JOIN_LINES = "TextEditor.JoinLines";
+const char * const INSERT_LINE_ABOVE = "TextEditor.InsertLineAboveCurrentLine";
+const char * const INSERT_LINE_BELOW = "TextEditor.InsertLineBelowCurrentLine";
const char * const CUT_LINE = "TextEditor.CutLine";
const char * const DELETE_LINE = "TextEditor.DeleteLine";
const char * const DELETE_WORD = "TextEditor.DeleteWord";
const char * const GOTO_PREVIOUS_WORD_WITH_SELECTION = "TextEditor.GotoPreviousWordWithSelection";
const char * const GOTO_NEXT_WORD_WITH_SELECTION = "TextEditor.GotoNextWordWithSelection";
const char * const C_TEXTEDITOR_MIMETYPE_TEXT = "text/plain";
-const char * const C_TEXTEDITOR_MIMETYPE_XML = "application/xml";
// Text color and style categories
#include "plaintexteditorfactory.h"
#include "plaintexteditor.h"
#include "storagesettings.h"
+#include "manager.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
{
QTC_ASSERT(!m_instance, return);
m_instance = this;
+
+ connect(Core::ICore::instance(), SIGNAL(coreOpened()),
+ Manager::instance(), SLOT(registerMimeTypes()));
}
TextEditorPlugin::~TextEditorPlugin()
void initializeEditor(PlainTextEditor *editor);
+ PlainTextEditorFactory *editorFactory() { return m_editorFactory; }
LineNumberFilter *lineNumberFilter() { return m_lineNumberFilter; }
private slots:
-<plugin name="VCSBase" version="1.3.84" compatVersion="1.3.84">
+<plugin name="VCSBase" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Version Control System Base Plugin</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
- <dependency name="TextEditor" version="1.3.84"/>
- <dependency name="ProjectExplorer" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
+ <dependency name="TextEditor" version="2.0.80"/>
+ <dependency name="ProjectExplorer" version="2.0.80"/>
</dependencyList>
</plugin>
{
}
+void SubmitEditorFile::rename(const QString &newName)
+{
+ Q_UNUSED(newName);
+ // We can't be renamed
+ return;
+}
+
void SubmitEditorFile::setFileName(const QString name)
{
m_fileName = name;
bool save(const QString &fileName);
ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const;
void reload(ReloadFlag flag, ChangeType type);
-
+ void rename(const QString &newName);
void setFileName(const QString name);
void setModified(bool modified = true);
// Try to locate via repository.
if (!control)
return QString();
- const QString topLevel = control->findTopLevelForDirectory(sourceDir);
- if (topLevel.isEmpty())
+ QString topLevel;
+ if (!control->managesDirectory(sourceDir, &topLevel))
return QString();
const QFileInfo topLevelFileInfo(topLevel + slash + f);
if (topLevelFileInfo.isFile())
-<plugin name="Welcome" version="1.3.84" compatVersion="1.3.84">
+<plugin name="Welcome" version="2.0.80" compatVersion="2.0.80">
<vendor>Nokia Corporation</vendor>
<copyright>(C) 2010 Nokia Corporation</copyright>
<license>
<description>Default Welcome Screen Plugin</description>
<url>http://qt.nokia.com</url>
<dependencyList>
- <dependency name="Core" version="1.3.84"/>
+ <dependency name="Core" version="2.0.80"/>
</dependencyList>
</plugin>
class TypenameArgument;
class Function;
class Namespace;
+class NamespaceAlias;
class BaseClass;
class Block;
class Class;
symbol->setTemplateParameters(_templateParameters);
_templateParameters = 0;
}
+
if (ty.isDeprecated())
symbol->setDeprecated(true);
+ if (ty.isFriend())
+ symbol->setStorage(Symbol::Friend);
+
_scope->enterSymbol(symbol);
return false;
}
Function *fun = 0;
if (declTy && 0 != (fun = declTy->asFunctionType())) {
- fun->setSourceLocation(location);
+ fun->setSourceLocation(location, translationUnit());
fun->setScope(_scope);
fun->setName(name);
fun->setMethodKey(semantic()->currentMethodKey());
fun->setStartOffset(tokenAt(ast->firstToken()).offset);
fun->setEndOffset(tokenAt(ast->lastToken()).offset);
if (ast->declarator)
- fun->setSourceLocation(ast->declarator->firstToken());
+ fun->setSourceLocation(ast->declarator->firstToken(), translationUnit());
fun->setName(name);
fun->setTemplateParameters(_templateParameters);
fun->setVisibility(semantic()->currentVisibility());
return false;
}
-bool CheckDeclaration::visit(NamespaceAliasDefinitionAST *)
+bool CheckDeclaration::visit(NamespaceAliasDefinitionAST *ast)
{
+ const Name *name = 0;
+
+ if (const Identifier *id = identifier(ast->namespace_name_token))
+ name = control()->nameId(id);
+
+ unsigned sourceLocation = ast->firstToken();
+
+ if (ast->namespace_name_token)
+ sourceLocation = ast->namespace_name_token;
+
+ const Name *namespaceName = semantic()->check(ast->name, _scope);
+
+ NamespaceAlias *namespaceAlias = control()->newNamespaceAlias(sourceLocation, name);
+ namespaceAlias->setNamespaceName(namespaceName);
+ namespaceAlias->setStartOffset(tokenAt(ast->firstToken()).offset);
+ namespaceAlias->setEndOffset(tokenAt(ast->lastToken()).offset);
+ //ast->symbol = namespaceAlias;
+ _scope->enterSymbol(namespaceAlias);
+
return false;
}
bool CheckName::visit(ObjCSelectorAST *ast)
{
std::vector<const Name *> names;
+ bool hasArgs = false;
for (ObjCSelectorArgumentListAST *it = ast->selector_argument_list; it; it = it->next) {
if (it->value->name_token) {
const Identifier *id = control()->findOrInsertIdentifier(spell(it->value->name_token));
const NameId *nameId = control()->nameId(id);
names.push_back(nameId);
+
+ if (!hasArgs && it->value->colon_token)
+ hasArgs = true;
} else {
// we have an incomplete name due, probably due to error recovery. So, back out completely
return false;
}
if (!names.empty()) {
- _name = control()->selectorNameId(&names[0], names.size(), true);
+ _name = control()->selectorNameId(&names[0], names.size(), hasArgs);
ast->name = _name;
}
return ns;
}
+ NamespaceAlias *newNamespaceAlias(unsigned sourceLocation, const Name *name)
+ {
+ NamespaceAlias *ns = new NamespaceAlias(translationUnit,
+ sourceLocation, name);
+ symbols.push_back(ns);
+ return ns;
+ }
+
UsingNamespaceDirective *newUsingNamespaceDirective(unsigned sourceLocation, const Name *name)
{
UsingNamespaceDirective *u = new UsingNamespaceDirective(translationUnit,
Namespace *Control::newNamespace(unsigned sourceLocation, const Name *name)
{ return d->newNamespace(sourceLocation, name); }
+NamespaceAlias *Control::newNamespaceAlias(unsigned sourceLocation, const Name *name)
+{ return d->newNamespaceAlias(sourceLocation, name); }
+
BaseClass *Control::newBaseClass(unsigned sourceLocation, const Name *name)
{ return d->newBaseClass(sourceLocation, name); }
/// Creates a new Namespace symbol.
Namespace *newNamespace(unsigned sourceLocation, const Name *name = 0);
+ /// Creates a new Namespace symbol.
+ NamespaceAlias *newNamespaceAlias(unsigned sourceLocation, const Name *name = 0);
+
/// Creates a new BaseClass symbol.
BaseClass *newBaseClass(unsigned sourceLocation, const Name *name = 0);
{
DEBUG_THIS_RULE();
+#ifdef CPLUSPLUS_WITH_CXXOX_INITIALIZER_LIST
if (_cxx0xEnabled)
return parseInitializerList0x(node);
-
-
+#endif
// ### remove me
ExpressionListAST **expression_list_ptr = &node;
return false;
}
+bool Scope::isObjCProtocolScope() const
+{
+ if (_owner)
+ return _owner->isObjCProtocol();
+ return false;
+}
+
bool Scope::isFunctionScope() const
{
Function *f = 0;
/// Returns true if this scope's owner is an ObjCClass Symbol.
bool isObjCClassScope() const;
+ /// Returns true if this scope's owner is an ObjCProtocol Symbol.
+ bool isObjCProtocolScope() const;
+
/// Returns true if this scope's owner is an ObjCMethod symbol.
bool isObjCMethodScope() const;
};
Symbol::Symbol(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name)
- : _control(translationUnit->control()),
- _sourceLocation(sourceLocation),
- _sourceOffset(0),
- _startOffset(0),
+ : _startOffset(0),
_endOffset(0),
_name(0),
_hashCode(0),
_next(0),
_isGenerated(false)
{
- setSourceLocation(sourceLocation);
+ setSourceLocation(sourceLocation, translationUnit);
setName(name);
}
Symbol::~Symbol()
{ }
-Control *Symbol::control() const
-{ return _control; }
-
-TranslationUnit *Symbol::translationUnit() const
-{ return _control->translationUnit(); }
-
void Symbol::visitSymbol(SymbolVisitor *visitor)
{
if (visitor->preVisit(this))
unsigned Symbol::sourceLocation() const
{ return _sourceLocation; }
-unsigned Symbol::sourceOffset() const
-{ return _sourceOffset; }
-
bool Symbol::isGenerated() const
{ return _isGenerated; }
void Symbol::setDeprecated(bool isDeprecated)
{ _isDeprecated = isDeprecated; }
-void Symbol::setSourceLocation(unsigned sourceLocation)
+void Symbol::setSourceLocation(unsigned sourceLocation, TranslationUnit *translationUnit)
{
_sourceLocation = sourceLocation;
- if (! _sourceLocation) {
- _isGenerated = false;
- _sourceOffset = 0;
- } else {
- TranslationUnit *unit = translationUnit();
-
- const Token &tk = unit->tokenAt(sourceLocation);
-
+ if (translationUnit) {
+ const Token &tk = translationUnit->tokenAt(sourceLocation);
_isGenerated = tk.f.generated;
- _sourceOffset = tk.offset;
+ translationUnit->getPosition(tk.offset, &_line, &_column, &_fileId);
+ } else {
+ _isGenerated = false;
+ _line = 0;
+ _column = 0;
+ _fileId = 0;
}
}
unsigned Symbol::line() const
{
- unsigned line = 0, column = 0;
- const StringLiteral *fileId = 0;
- translationUnit()->getPosition(_sourceOffset, &line, &column, &fileId);
- return line;
+ return _line;
}
unsigned Symbol::column() const
{
- unsigned line = 0, column = 0;
- const StringLiteral *fileId = 0;
- translationUnit()->getPosition(_sourceOffset, &line, &column, &fileId);
- return column;
+ return _column;
}
const StringLiteral *Symbol::fileId() const
{
- unsigned line = 0, column = 0;
- const StringLiteral *fileId = 0;
- translationUnit()->getPosition(_sourceOffset, &line, &column, &fileId);
- return fileId;
+ return _fileId;
}
-void Symbol::getPosition(unsigned *line, unsigned *column, const StringLiteral **fileId) const
-{ translationUnit()->getPosition(_sourceOffset, line, column, fileId); }
-
-void Symbol::getStartPosition(unsigned *line, unsigned *column, const StringLiteral **fileId) const
-{ translationUnit()->getPosition(_startOffset, line, column, fileId); }
-
-void Symbol::getEndPosition(unsigned *line, unsigned *column, const StringLiteral **fileId) const
-{ translationUnit()->getPosition(_endOffset, line, column, fileId); }
-
const char *Symbol::fileName() const
{ return fileId()->chars(); }
bool Symbol::isObjCPropertyDeclaration() const
{ return asObjCPropertyDeclaration() != 0; }
+
+void Symbol::copy(Symbol *other)
+{
+ _sourceLocation = other->_sourceLocation;
+ _startOffset = other->_startOffset;
+ _endOffset = other->_endOffset;
+ _name = other->_name;
+ _hashCode = other->_hashCode;
+ _storage = other->_storage;
+ _visibility = other->_visibility;
+ _scope = other->_scope;
+ _index = other->_index;
+ _next = other->_next;
+ _fileId = other->_fileId;
+ _line = other->_line;
+ _column = other->_column;
+
+ _isGenerated = other->_isGenerated;
+ _isDeprecated = other->_isDeprecated;
+}
/// Destroy this Symbol.
virtual ~Symbol();
- /// Returns this Symbol's Control object.
- Control *control() const;
-
/// Returns this Symbol's source location.
unsigned sourceLocation() const;
- /// Returns this Symbol's source offset.
- unsigned sourceOffset() const;
-
/// Returns this Symbol's line number.
unsigned line() const;
unsigned endOffset() const;
void setEndOffset(unsigned offset);
- void getPosition(unsigned *line, unsigned *column = 0, const StringLiteral **fileId = 0) const;
- void getStartPosition(unsigned *line, unsigned *column = 0, const StringLiteral **fileId = 0) const;
- void getEndPosition(unsigned *line, unsigned *column = 0, const StringLiteral **fileId = 0) const;
-
/// Returns this Symbol's name.
const Name *name() const;
virtual const Enum *asEnum() const { return 0; }
virtual const Function *asFunction() const { return 0; }
virtual const Namespace *asNamespace() const { return 0; }
+ virtual const NamespaceAlias *asNamespaceAlias() const { return 0; }
virtual const Class *asClass() const { return 0; }
virtual const Block *asBlock() const { return 0; }
virtual const UsingNamespaceDirective *asUsingNamespaceDirective() const { return 0; }
virtual Enum *asEnum() { return 0; }
virtual Function *asFunction() { return 0; }
virtual Namespace *asNamespace() { return 0; }
+ virtual NamespaceAlias *asNamespaceAlias() { return 0; }
virtual Class *asClass() { return 0; }
virtual Block *asBlock() { return 0; }
virtual UsingNamespaceDirective *asUsingNamespaceDirective() { return 0; }
Scope *enclosingBlockScope() const;
void setScope(Scope *scope); // ### make me private
- void setSourceLocation(unsigned sourceLocation); // ### make me private
+ void setSourceLocation(unsigned sourceLocation, TranslationUnit *translationUnit); // ### make me private
void visitSymbol(SymbolVisitor *visitor);
static void visitSymbol(Symbol *symbol, SymbolVisitor *visitor);
+ virtual void copy(Symbol *other);
+
protected:
virtual void visitSymbol0(SymbolVisitor *visitor) = 0;
- TranslationUnit *translationUnit() const;
-
private:
- Control *_control;
unsigned _sourceLocation;
- unsigned _sourceOffset;
unsigned _startOffset;
unsigned _endOffset;
const Name *_name;
Scope *_scope;
unsigned _index;
Symbol *_next;
+ const StringLiteral *_fileId;
+ unsigned _line;
+ unsigned _column;
bool _isGenerated: 1;
bool _isDeprecated: 1;
virtual bool visit(UsingNamespaceDirective *) { return true; }
virtual bool visit(UsingDeclaration *) { return true; }
+ virtual bool visit(NamespaceAlias *) { return true; }
virtual bool visit(Declaration *) { return true; }
virtual bool visit(Argument *) { return true; }
virtual bool visit(TypenameArgument *) { return true; }
void UsingNamespaceDirective::visitSymbol0(SymbolVisitor *visitor)
{ visitor->visit(this); }
+NamespaceAlias::NamespaceAlias(TranslationUnit *translationUnit,
+ unsigned sourceLocation, const Name *name)
+ : Symbol(translationUnit, sourceLocation, name), _namespaceName(0)
+{ }
+
+NamespaceAlias::~NamespaceAlias()
+{ }
+
+const Name *NamespaceAlias::namespaceName() const
+{ return _namespaceName; }
+
+void NamespaceAlias::setNamespaceName(const Name *namespaceName)
+{ _namespaceName = namespaceName; }
+
+FullySpecifiedType NamespaceAlias::type() const
+{ return FullySpecifiedType(); }
+
+void NamespaceAlias::visitSymbol0(SymbolVisitor *visitor)
+{ visitor->visit(this); }
+
+
UsingDeclaration::UsingDeclaration(TranslationUnit *translationUnit,
unsigned sourceLocation, const Name *name)
: Symbol(translationUnit, sourceLocation, name)
virtual void visitSymbol0(SymbolVisitor *visitor);
};
+class CPLUSPLUS_EXPORT NamespaceAlias: public Symbol
+{
+public:
+ NamespaceAlias(TranslationUnit *translationUnit, unsigned sourceLocation, const Name *name);
+ virtual ~NamespaceAlias();
+
+ const Name *namespaceName() const;
+ void setNamespaceName(const Name *namespaceName);
+
+ // Symbol's interface
+ virtual FullySpecifiedType type() const;
+
+ virtual const NamespaceAlias *asNamespaceAlias() const
+ { return this; }
+
+ virtual NamespaceAlias *asNamespaceAlias()
+ { return this; }
+
+protected:
+ virtual void visitSymbol0(SymbolVisitor *visitor);
+
+private:
+ const Name *_namespaceName;
+};
+
class CPLUSPLUS_EXPORT Declaration: public Symbol
{
public:
# profilereader \
aggregation \
changeset \
- icheckbuild
+ icheckbuild \
+ generichighlighter
contains (QT_CONFIG, declarative) {
SUBDIRS += qml
Snapshot snapshot;
snapshot.insert(doc);
- Document::Ptr emptyDoc = Document::create("<empty>");
-
Class *baseClass = doc->globalSymbolAt(0)->asClass();
QVERIFY(baseClass);
Class *derivedClass = doc->globalSymbolAt(1)->asClass();
QVERIFY(derivedClass);
- LookupContext ctx(derivedClass, emptyDoc, doc, snapshot);
+ const LookupContext ctx(doc, snapshot);
- const QList<Symbol *> candidates =
- ctx.resolveClass(derivedClass->baseClassAt(0)->name());
+ ClassOrNamespace *klass = ctx.lookupType(derivedClass->baseClassAt(0)->name(), derivedClass->scope());
+ QVERIFY(klass != 0);
- QCOMPARE(candidates.size(), 1);
- QCOMPARE(candidates.at(0), baseClass);
+ QCOMPARE(klass->symbols().size(), 1);
+ QCOMPARE(klass->symbols().first(), baseClass);
TranslationUnit *unit = doc->translationUnit();
QVERIFY(unit != 0);
Snapshot snapshot;
snapshot.insert(doc);
- Document::Ptr emptyDoc = Document::create("<empty>");
-
ObjCClass *iface = doc->globalSymbolAt(0)->asObjCClass();
QVERIFY(iface);
QVERIFY(iface->isInterface());
QVERIFY(!impl->isInterface());
QCOMPARE(impl->memberCount(), 3U);
- ObjCMethod *allocMethod = impl->memberAt(0)->asObjCMethod();
- QVERIFY(allocMethod);
- QVERIFY(allocMethod->name() && allocMethod->name()->identifier());
- QCOMPARE(QLatin1String(allocMethod->name()->identifier()->chars()), QLatin1String("alloc"));
+ Declaration *allocMethodIface = iface->memberAt(0)->asDeclaration();
+ QVERIFY(allocMethodIface);
+ QVERIFY(allocMethodIface->name() && allocMethodIface->name()->identifier());
+ QCOMPARE(QLatin1String(allocMethodIface->name()->identifier()->chars()), QLatin1String("alloc"));
+
+ ObjCMethod *allocMethodImpl = impl->memberAt(0)->asObjCMethod();
+ QVERIFY(allocMethodImpl);
+ QVERIFY(allocMethodImpl->name() && allocMethodImpl->name()->identifier());
+ QCOMPARE(QLatin1String(allocMethodImpl->name()->identifier()->chars()), QLatin1String("alloc"));
ObjCMethod *deallocMethod = impl->memberAt(2)->asObjCMethod();
QVERIFY(deallocMethod);
QVERIFY(deallocMethod->name() && deallocMethod->name()->identifier());
QCOMPARE(QLatin1String(deallocMethod->name()->identifier()->chars()), QLatin1String("dealloc"));
- const LookupContext ctxt(impl, emptyDoc, doc, snapshot);
+ const LookupContext context(doc, snapshot);
// check class resolving:
- const QList<Symbol *> candidates = ctxt.resolveObjCClass(impl->name());
- QCOMPARE(candidates.size(), 2);
- QVERIFY(candidates.contains(iface));
- QVERIFY(candidates.contains(impl));
-
- // check scope expansion:
- QList<Scope *> expandedScopes;
- ctxt.expand(impl->members(), ctxt.visibleScopes(), &expandedScopes);
- QCOMPARE(expandedScopes.size(), 2);
-
- const ResolveExpression resolver(ctxt);
+ ClassOrNamespace *klass = context.lookupType(impl->name(), impl->scope());
+ QVERIFY(klass != 0);
+ QCOMPARE(klass->symbols().size(), 2);
+ QVERIFY(klass->symbols().contains(iface));
+ QVERIFY(klass->symbols().contains(impl));
// check method resolving:
- QList<LookupItem> results = resolver.resolveMember(allocMethod->name(), impl);
+ QList<Symbol *> results = context.lookup(allocMethodImpl->name(), impl->members());
QCOMPARE(results.size(), 2);
- QVERIFY(results.at(0).lastVisibleSymbol() == allocMethod || results.at(1).lastVisibleSymbol() == allocMethod);
- QVERIFY(results.at(0).lastVisibleSymbol()->asDeclaration() || results.at(1).lastVisibleSymbol()->asDeclaration());
+ QCOMPARE(results.at(0), allocMethodIface);
+ QCOMPARE(results.at(1), allocMethodImpl);
- results = resolver.resolveMember(deallocMethod->name(), impl);
+ results = context.lookup(deallocMethod->name(), impl->members());
QCOMPARE(results.size(), 1);
- QCOMPARE(results.at(0).lastVisibleSymbol(), deallocMethod);
+ QCOMPARE(results.at(0), deallocMethod);
}
void tst_Lookup::class_with_baseclass()
QVERIFY(baseMethod->name() && baseMethod->name()->identifier());
QCOMPARE(QLatin1String(baseMethod->name()->identifier()->chars()), QLatin1String("baseMethod"));
- const LookupContext ctxt(zooImpl, emptyDoc, doc, snapshot);
-
- const QList<Symbol *> candidates = ctxt.resolveObjCClass(baseZoo->name());
- QCOMPARE(candidates.size(), 1);
- QVERIFY(candidates.contains(baseZoo));
-
- QList<Scope *> expandedScopes;
- ctxt.expand(zooImpl->members(), ctxt.visibleScopes(), &expandedScopes);
- QCOMPARE(expandedScopes.size(), 3);
+ const LookupContext context(doc, snapshot);
- const ResolveExpression resolver(ctxt);
+ ClassOrNamespace *objClass = context.lookupType(baseZoo->name(), zooImpl->scope());
+ QVERIFY(objClass != 0);
+ QVERIFY(objClass->symbols().contains(baseZoo));
- QList<LookupItem> results = resolver.resolveMember(baseDecl->name(), zooImpl);
+ QList<Symbol *> results = context.lookup(baseDecl->name(), zooImpl->members());
QCOMPARE(results.size(), 1);
- QCOMPARE(results.at(0).lastVisibleSymbol(), baseDecl);
+ QCOMPARE(results.at(0), baseDecl);
- results = resolver.resolveMember(baseMethod->name(), zooImpl);
+ results = context.lookup(baseMethod->name(), zooImpl->members());
QCOMPARE(results.size(), 1);
- QCOMPARE(results.at(0).lastVisibleSymbol(), baseMethod);
+ QCOMPARE(results.at(0), baseMethod);
}
void tst_Lookup::class_with_protocol_with_protocol()
Snapshot snapshot;
snapshot.insert(doc);
- Document::Ptr emptyDoc = Document::create("<empty>");
-
ObjCProtocol *P1 = doc->globalSymbolAt(0)->asObjCProtocol();
QVERIFY(P1);
QCOMPARE(P1->memberCount(), 1U);
ObjCClass *zooImpl = doc->globalSymbolAt(3)->asObjCClass();
QVERIFY(zooImpl);
- const LookupContext ctxt(zooImpl, emptyDoc, doc, snapshot);
+ const LookupContext context(doc, snapshot);
{
- const QList<Symbol *> candidates = ctxt.resolveObjCProtocol(P1->name());
+ const QList<Symbol *> candidates = context.lookup(P1->name(), zooImpl->scope());
QCOMPARE(candidates.size(), 1);
QVERIFY(candidates.contains(P1));
}
{
- const QList<Symbol *> candidates = ctxt.resolveObjCProtocol(P2->protocolAt(0)->name());
+ const QList<Symbol *> candidates = context.lookup(P2->protocolAt(0)->name(), zooImpl->scope());
QCOMPARE(candidates.size(), 1);
QVERIFY(candidates.contains(P1));
}
- QList<Scope *> expandedScopes;
- ctxt.expand(zooImpl->members(), ctxt.visibleScopes(), &expandedScopes);
- QCOMPARE(expandedScopes.size(), 4);
-
- const ResolveExpression resolver(ctxt);
-
- QList<LookupItem> results = resolver.resolveMember(p1method->name(), zooImpl);
+ QList<Symbol *> results = context.lookup(p1method->name(), zooImpl->members());
QCOMPARE(results.size(), 1);
- QCOMPARE(results.at(0).lastVisibleSymbol(), p1method);
+ QCOMPARE(results.at(0), p1method);
}
void tst_Lookup::iface_impl_scoping()
Snapshot snapshot;
snapshot.insert(doc);
- Document::Ptr emptyDoc = Document::create("<empty>");
ObjCClass *iface = doc->globalSymbolAt(0)->asObjCClass();
QVERIFY(iface);
QVERIFY(iface->isInterface());
Block *method1Body = method1Impl->memberAt(0)->asBlock();
QVERIFY(method1Body);
- const LookupContext ctxt(method1Body, emptyDoc, doc, snapshot);
+ const LookupContext context(doc, snapshot);
{ // verify if we can resolve "arg" in the body
QCOMPARE(method1Impl->argumentCount(), 1U);
QVERIFY(arg->name()->identifier());
QCOMPARE(arg->name()->identifier()->chars(), "arg");
- const QList<Symbol *> candidates = ctxt.resolve(arg->name());
+ const QList<Symbol *> candidates = context.lookup(arg->name(), method1Body->scope());
QCOMPARE(candidates.size(), 1);
QVERIFY(candidates.at(0)->type()->asIntegerType());
}
QCOMPARE(method2->identifier()->chars(), "method2");
{ // verify if we can resolve "method2" in the body
- const QList<Symbol *> candidates = ctxt.resolve(method2->name());
+ const QList<Symbol *> candidates = context.lookup(method2->name(), method1Body->scope());
QCOMPARE(candidates.size(), 1);
QCOMPARE(candidates.at(0), method2);
}
-
- { // now let's see if the resolver can do the same for method2
- const ResolveExpression resolver(ctxt);
-
- const QList<LookupItem> results = resolver.resolveMember(method2->name(),
- impl);
- QCOMPARE(results.size(), 1);
- QCOMPARE(results.at(0).lastVisibleSymbol(), method2);
- }
}
QTEST_APPLESS_MAIN(tst_Lookup)
#include <Names.h>
#include <Literals.h>
#include <DiagnosticClient.h>
-#include <GenTemplateInstance.h>
+#include <DeprecatedGenTemplateInstance.h>
#include <Overview.h>
#include <ExpressionUnderCursor.h>
#include <Names.h>
void tst_Semantic::template_instance_1()
{
- QSharedPointer<Document> doc = document("void append(const _Tp &value);");
+ QSharedPointer<Document> doc = document("template <typename _Tp> class QList { void append(const _Tp &value); };");
QCOMPARE(doc->errorCount, 0U);
QCOMPARE(doc->globals->symbolCount(), 1U);
- Declaration *decl = doc->globals->symbolAt(0)->asDeclaration();
+ Declaration *decl = doc->globals->symbolAt(0)->asClass()->memberAt(0)->asDeclaration();
QVERIFY(decl);
- GenTemplateInstance::Substitution subst;
- const Identifier *nameTp = control.findOrInsertIdentifier("_Tp");
- FullySpecifiedType intTy(control.integerType(IntegerType::Int));
- subst.append(qMakePair(nameTp, intTy));
+ FullySpecifiedType templArgs[] = { control.integerType(IntegerType::Int) };
+ const Name *templId = control.templateNameId(control.findOrInsertIdentifier("QList"), templArgs, 1);
- GenTemplateInstance inst(&control, subst);
- FullySpecifiedType genTy = inst(decl);
+ FullySpecifiedType genTy = DeprecatedGenTemplateInstance::instantiate(templId, decl, &control);
Overview oo;
oo.setShowReturnTypes(true);
const QString genDecl = oo.prettyType(genTy);
- QCOMPARE(genDecl, QString::fromLatin1("void(const int &)"));
+ QCOMPARE(genDecl, QString::fromLatin1("void (const int &)"));
}
void tst_Semantic::expression_under_cursor_1()
SOURCES += \
$$DEBUGGERDIR/gdb/gdbmi.cpp \
+ $$DEBUGGERDIR/tcf/json.cpp \
$$MACROSDIR/gdbmacros.cpp \
tst_dumpers.cpp \
#include "gdb/gdbmi.h"
+#include "tcf/json.h"
#include "gdbmacros.h"
#include "gdbmacros_p.h"
"numchild=\"0\"}]";
+static const char jsont1[] =
+ "{\"Size\":100564,\"UID\":0,\"GID\":0,\"Permissions\":33261,"
+ "\"ATime\":1242370878000,\"MTime\":1239154689000}";
+
struct Int3 {
Int3() { i1 = 42; i2 = 43; i3 = 44; }
int i1, i2, i3;
'\n' + QString(input));
}
+ void testJson(const char* input)
+ {
+ QCOMPARE('\n' + QString::fromLatin1(JsonValue(input).toString(false)),
+ '\n' + QString(input));
+ }
+
private slots:
void mi1() { testMi(gdbmi1); }
void mi2() { testMi(gdbmi2); }
void mi11() { testMi(gdbmi11); }
//void mi12() { testMi(gdbmi12); }
+ void json1() { testJson(jsont1); }
+
void infoBreak();
void niceType();
void niceType_data();
--- /dev/null
+TEMPLATE = subdirs
+SUBDIRS += specificrules
--- /dev/null
+QT += testlib
+
+PLUGINSDIR = ../../../../src/plugins
+
+SOURCES += tst_specificrules.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/context.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/dynamicrule.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/rule.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/specificrules.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/progressdata.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/highlightdefinition.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/keywordlist.cpp \
+ $$PLUGINSDIR/texteditor/generichighlighter/itemdata.cpp
+
+INCLUDEPATH += $$PLUGINSDIR
+
+TARGET=tst_$$TARGET
--- /dev/null
+/**************************************************************************
+**
+** 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 <texteditor/generichighlighter/highlightdefinition.h>
+#include <texteditor/generichighlighter/keywordlist.h>
+#include <texteditor/generichighlighter/specificrules.h>
+#include <texteditor/generichighlighter/progressdata.h>
+
+#include <QtTest/QtTest>
+
+using namespace TextEditor;
+using namespace Internal;
+
+class tst_SpecificRules : public QObject
+{
+ Q_OBJECT
+public:
+ tst_SpecificRules() : m_definition(new HighlightDefinition) {}
+
+private slots:
+ void initTestCase();
+
+ void testDetectChar();
+ void testDetectChar_data();
+
+ void testDetect2Char();
+ void testDetect2Char_data();
+
+ void testAnyChar();
+ void testAnyChar_data();
+
+ void testStringDetect();
+ void testStringDetect_data();
+
+ void testRegExpr();
+ void testRegExpr_data();
+ void testRegExprOffsetIncremented();
+ void testRegExprOffsetIncremented_data();
+
+ void testKeywordGlobalSensitiveLocalSensitive();
+ void testKeywordGlobalSensitiveLocalSensitive_data();
+ void testKeywordGlobalSensitiveLocalInsensitive();
+ void testKeywordGlobalSensitiveLocalInsensitive_data();
+ void testKeywordGlobalInsensitiveLocalInsensitive();
+ void testKeywordGlobalInsensitiveLocalInsensitive_data();
+ void testKeywordGlobalInsensitiveLocalSensitive();
+ void testKeywordGlobalInsensitiveLocalSensitive_data();
+
+ void testInt();
+ void testInt_data();
+
+ void testFloat();
+ void testFloat_data();
+
+ void testCOctal();
+ void testCOctal_data();
+
+ void testCHex();
+ void testCHex_data();
+
+ void testCString();
+ void testCString_data();
+
+ void testCChar();
+ void testCChar_data();
+
+ void testRangeDetect();
+ void testRangeDetect_data();
+
+ void testLineContinue();
+ void testLineContinue_data();
+
+ void testDetectSpaces();
+ void testDetectSpaces_data();
+
+ void testDetectIdentifier();
+ void testDetectIdentifier_data();
+
+private:
+ void addCommonColumns() const;
+ void testMatch(const Rule &rule) const;
+ void testMatch(const Rule &rule, ProgressData *progress) const;
+
+ void noMatchForInt() const;
+ void noMatchForFloat() const;
+ void noMatchForCOctal() const;
+ void noMatchForCHex() const;
+ void noMatchForNumber() const;
+
+ void commonCasesForKeywords() const;
+
+ QSharedPointer<HighlightDefinition> m_definition;
+};
+
+void tst_SpecificRules::initTestCase()
+{
+ QSharedPointer<KeywordList> list = m_definition->createKeywordList("keywords");
+ list->addKeyword("for");
+ list->addKeyword("while");
+ list->addKeyword("BEGIN");
+ list->addKeyword("END");
+ list->addKeyword("WeIrD");
+}
+
+void tst_SpecificRules::addCommonColumns() const
+{
+ QTest::addColumn<QString>("s");
+ QTest::addColumn<bool>("match");
+ QTest::addColumn<int>("offset");
+ QTest::addColumn<bool>("only spaces");
+ QTest::addColumn<bool>("will continue");
+}
+
+void tst_SpecificRules::testMatch(const Rule &rule) const
+{
+ ProgressData progress;
+ testMatch(rule, &progress);
+}
+
+void tst_SpecificRules::testMatch(const Rule &rule, ProgressData *progress) const
+{
+ QFETCH(QString, s);
+
+ QTEST(rule.matchSucceed(s, s.length(), progress), "match");
+ QTEST(progress->offset(), "offset");
+ QTEST(progress->onlySpacesSoFar(), "only spaces");
+ QTEST(progress->willContinueLine(), "will continue");
+}
+
+void tst_SpecificRules::testDetectChar()
+{
+ QFETCH(QString, c);
+ DetectCharRule rule;
+ rule.setChar(c);
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testDetectChar_data()
+{
+ QTest::addColumn<QString>("c");
+ addCommonColumns();
+
+ QTest::newRow("[#] against [#]") << "#" << "#" << true << 1 << false << false;
+ QTest::newRow("[#] against [##]") << "#" << "##" << true << 1 << false << false;
+ QTest::newRow("[#] against [ ]") << "#" << " " << false << 0 << true << false;
+ QTest::newRow("[#] against [a]") << "#" << "a" << false << 0 << true << false;
+ QTest::newRow("[#] against [abc]") << "#" << "abc" << false << 0 << true << false;
+ QTest::newRow("[#] against [x#]") << "#" << "x#" << false << 0 << true << false;
+ QTest::newRow("[ ] against [a]") << " " << "a" << false << 0 << true << false;
+ //QTest::newRow("[ ] against [ ]") << " " << " " << true << 1 << true << false;
+}
+
+void tst_SpecificRules::testDetect2Char()
+{
+ QFETCH(QString, c);
+ QFETCH(QString, c1);
+ Detect2CharsRule rule;
+ rule.setChar(c);
+ rule.setChar1(c1);
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testDetect2Char_data()
+{
+ QTest::addColumn<QString>("c");
+ QTest::addColumn<QString>("c1");
+ addCommonColumns();
+
+ QTest::newRow("[//] against [//]") << "/" << "/" << "//" << true << 2 << false << false;
+ QTest::newRow("[//] against [///]") << "/" << "/" << "///" << true << 2 << false << false;
+ QTest::newRow("[//] against [// ]") << "/" << "/" << "// " << true << 2 << false << false;
+ QTest::newRow("[//] against [ //]") << "/" << "/" << " //" << false << 0 << true << false;
+ QTest::newRow("[//] against [a]") << "/" << "/" << "a" << false << 0 << true << false;
+ QTest::newRow("[//] against [ a]") << "/" << "/" << " a" << false << 0 << true << false;
+ QTest::newRow("[//] against [abc]") << "/" << "/" << "abc" << false << 0 << true << false;
+ QTest::newRow("[//] against [/a]") << "/" << "/" << "/a" << false << 0 << true << false;
+ QTest::newRow("[//] against [a/]") << "/" << "/" << "a/" << false << 0 << true << false;
+ QTest::newRow("[ ] against [xx]") << " " << " " << "xx" << false << 0 << true << false;
+ //QTest::newRow("[ ] against [ ]") << " " << " " << " " << true << 3 << true << false;
+}
+
+void tst_SpecificRules::testAnyChar()
+{
+ QFETCH(QString, chars);
+ AnyCharRule rule;
+ rule.setCharacterSet(chars);
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testAnyChar_data()
+{
+ QTest::addColumn<QString>("chars");
+ addCommonColumns();
+
+ QTest::newRow("[:!<>?] against [:]") << ":!<>?" << ":" << true << 1 << false << false;
+ QTest::newRow("[:!<>?] against [!]") << ":!<>?" << "!" << true << 1 << false << false;
+ QTest::newRow("[:!<>?] against [<]") << ":!<>?" << "<" << true << 1 << false << false;
+ QTest::newRow("[:!<>?] against [>]") << ":!<>?" << ">" << true << 1 << false << false;
+ QTest::newRow("[:!<>?] against [?]") << ":!<>?" << "?" << true << 1 << false << false;
+ QTest::newRow("[:!<>?] against [:]") << ":!<>?" << ":" << true << 1 << false << false;
+ QTest::newRow("[:!<>?] against [ ]") << ":!<>?" << " " << false << 0 << true << false;
+ QTest::newRow("[:!<>?] against [#]") << ":!<>?" << "#" << false << 0 << true << false;
+ QTest::newRow("[:!<>?] against [!#]") << ":!<>?" << "!#" << true << 1 << false << false;
+ QTest::newRow("[:!<>?] against [#!]") << ":!<>?" << "#!" << false << 0 << true << false;
+ QTest::newRow("[:] against [:]") << ":" << ":" << true << 1 << false << false;
+ QTest::newRow("[:] against [#]") << ":" << "#" << false << 0 << true << false;
+ //QTest::newRow("[ ] against [ ]") << " " << " " << true << 1 << true << false;
+}
+
+void tst_SpecificRules::testStringDetect()
+{
+ QFETCH(QString, referenceString);
+ QFETCH(QString, insensitive);
+ StringDetectRule rule;
+ rule.setString(referenceString);
+ rule.setInsensitive(insensitive);
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testStringDetect_data()
+{
+ QTest::addColumn<QString>("referenceString");
+ QTest::addColumn<QString>("insensitive");
+ addCommonColumns();
+
+ QTest::newRow("[LL] against [LL]") << "LL" << "0" << "LL" << true << 2 << false << false;
+ QTest::newRow("[LL] against [ll]") << "LL" << "0" << "ll" << false << 0 << true << false;
+ QTest::newRow("[LL] against [ll] i") << "LL" << "1" << "ll" << true << 2 << false << false;
+ QTest::newRow("[ll] against [ll] i") << "LL" << "1" << "LL" << true << 2 << false << false;
+ QTest::newRow("[LL] against [5LL]") << "LL" << "0" << "5LL" << false << 0 << true << false;
+ QTest::newRow("[LL] against [L]") << "LL" << "0" << "L" << false << 0 << true << false;
+ QTest::newRow("[LL] against [LLL]") << "LL" << "0" << "LLL" << true << 2 << false << false;
+ QTest::newRow("[LL] against [ ]") << "LL" << "0" << " " << false << 0 << true << false;
+ QTest::newRow("[LL] against [xLLx]") << "LL" << "0" << "xLLx" << false << 0 << true << false;
+ QTest::newRow("[\"\"\"] against [\"\"\"]") << "\"\"\"" << "0" << "\"\"\"" << true << 3
+ << false << false;
+}
+
+void tst_SpecificRules::testRegExpr()
+{
+ QFETCH(QString, pattern);
+ QFETCH(QString, insensitive);
+ QFETCH(QString, minimal);
+ RegExprRule rule;
+ rule.setPattern(pattern);
+ rule.setInsensitive(insensitive);
+ rule.setMinimal(minimal);
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testRegExpr_data()
+{
+ QTest::addColumn<QString>("pattern");
+ QTest::addColumn<QString>("insensitive");
+ QTest::addColumn<QString>("minimal");
+ addCommonColumns();
+
+ QTest::newRow("[#[a-z]+\\s+\\d] against [#as 9]") << "#[a-z]+\\s+\\d" << "0" << "0"
+ << "#as 9" << true << 5 << false << false;
+ QTest::newRow("[#[a-z]+\\s+\\d] against [#As 9]") << "#[a-z]+\\s+\\d" << "0" << "0"
+ << "#As 9" << false << 0 << true << false;
+ QTest::newRow("[#[a-z]+\\s+\\d] against [#As 9] i") << "#[a-z]+\\s+\\d" << "1" << "0"
+ << "#As 9" << true << 5 << false << false;
+ QTest::newRow("[#[a-z]+\\s+\\d] against [as 9]") << "#[a-z]+\\s+\\d" << "0" << "0"
+ << "as 9" << false << 0 << true << false;
+ QTest::newRow("[#[a-z]+\\s+\\d] against [w#as 9]") << "#[a-z]+\\s+\\d" << "0" << "0"
+ << "w#as 9" << false << 0 << true << false;
+ QTest::newRow("[^\\s+[a-z]] against [x]") << "^\\s+[a-z]" << "0" << "0"
+ << "x" << false << 0 << true << false;
+ QTest::newRow("[^\\s+[a-z]] against [ x]") << "^\\s+[a-z]" << "0" << "0"
+ << " x" << true << 3 << false << false;
+ QTest::newRow("[0+] against [1001]") << "0+" << "0" << "0"
+ << "1001" << false << 0 << true << false;
+ QTest::newRow("[0+] against [001]") << "0+" << "0" << "0"
+ << "001" << true << 2 << false << false;
+ QTest::newRow("[0+] against [001]") << "0+" << "0" << "1"
+ << "001" << true << 1 << false << false;
+ QTest::newRow("[\\s*] against []") << "\\s*" << "0" << "0"
+ << "" << false << 0 << true << false;
+ //QTest::newRow("[\\s*] against []") << "\\s*" << "0" << "0"
+ // << " " << true << 1 << true << false;
+}
+
+void tst_SpecificRules::testRegExprOffsetIncremented()
+{
+ QFETCH(QString, pattern);
+ RegExprRule rule;
+ rule.setPattern(pattern);
+
+ ProgressData progress;
+ progress.setOffset(1);
+
+ testMatch(rule, &progress);
+}
+
+void tst_SpecificRules::testRegExprOffsetIncremented_data()
+{
+ QTest::addColumn<QString>("pattern");
+ addCommonColumns();
+
+ // To make sure that QRegExp::CaretAtZero is set.
+ QTest::newRow("[^\\s+[a-z]] against [ x]") << "^\\s+[a-z]" << " x" << false << 1
+ << true << false;
+}
+
+void tst_SpecificRules::commonCasesForKeywords() const
+{
+ QTest::newRow("[for]") << "for" << true << 3 << false << false;
+ QTest::newRow("[while]") << "while" << true << 5 << false << false;
+ QTest::newRow("[BEGIN]") << "BEGIN" << true << 5 << false << false;
+ QTest::newRow("[END]") << "END" << true << 3 << false << false;
+ QTest::newRow("[WeIrD]") << "WeIrD" << true << 5 << false << false;
+ QTest::newRow("[forr]") << "forr" << false << 0 << true << false;
+ QTest::newRow("[for#]") << "for#" << false << 0 << true << false;
+ QTest::newRow("[abc]") << "abc" << false << 0 << true << false;
+ QTest::newRow("[ ]") << " " << false << 0 << true << false;
+ QTest::newRow("[foe]") << "foe" << false << 0 << true << false;
+ QTest::newRow("[sor]") << "sor" << false << 0 << true << false;
+ QTest::newRow("[ffor]") << "ffor" << false << 0 << true << false;
+
+ // Valid default delimiters.
+ QTest::newRow("[for ]") << "for " << true << 3 << false << false;
+ QTest::newRow("[for.for]") << "for.for" << true << 3 << false << false;
+ QTest::newRow("[for(]") << "for(" << true << 3 << false << false;
+ QTest::newRow("[for)]") << "for)" << true << 3 << false << false;
+ QTest::newRow("[for:]") << "for:" << true << 3 << false << false;
+ QTest::newRow("[for!]") << "for!" << true << 3 << false << false;
+ QTest::newRow("[for+]") << "for+" << true << 3 << false << false;
+ QTest::newRow("[for,]") << "for," << true << 3 << false << false;
+ QTest::newRow("[for-]") << "for-" << true << 3 << false << false;
+ QTest::newRow("[for<]") << "for>" << true << 3 << false << false;
+ QTest::newRow("[for=]") << "for=" << true << 3 << false << false;
+ QTest::newRow("[for>]") << "for>" << true << 3 << false << false;
+ QTest::newRow("[for%]") << "for%" << true << 3 << false << false;
+ QTest::newRow("[for&]") << "for&" << true << 3 << false << false;
+ QTest::newRow("[for/]") << "for/" << true << 3 << false << false;
+ QTest::newRow("[for;]") << "for;" << true << 3 << false << false;
+ QTest::newRow("[for?]") << "for?" << true << 3 << false << false;
+ QTest::newRow("[for[]") << "for[" << true << 3 << false << false;
+ QTest::newRow("[for]]") << "for]" << true << 3 << false << false;
+ QTest::newRow("[for^]") << "for^" << true << 3 << false << false;
+ QTest::newRow("[for{]") << "for{" << true << 3 << false << false;
+ QTest::newRow("[for|]") << "for|" << true << 3 << false << false;
+ QTest::newRow("[for}]") << "for}" << true << 3 << false << false;
+ QTest::newRow("[for~]") << "for~" << true << 3 << false << false;
+ QTest::newRow("[for\\]") << "for\\" << true << 3 << false << false;
+ QTest::newRow("[for*]") << "for*" << true << 3 << false << false;
+ QTest::newRow("[for,for]") << "for,for" << true << 3 << false << false;
+ QTest::newRow("[for\t]") << "for\t" << true << 3 << false << false;
+}
+
+void tst_SpecificRules::testKeywordGlobalSensitiveLocalSensitive()
+{
+ m_definition->setKeywordsSensitive("true");
+ KeywordRule rule(m_definition);
+ rule.setInsensitive("false");
+ rule.setList("keywords");
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testKeywordGlobalSensitiveLocalSensitive_data()
+{
+ addCommonColumns();
+
+ commonCasesForKeywords();
+ QTest::newRow("[fOr]") << "fOr" << false << 0 << true << false;
+ QTest::newRow("[whilE") << "whilE" << false << 0 << true << false;
+ QTest::newRow("[bEGIN]") << "bEGIN" << false << 0 << true << false;
+ QTest::newRow("[end]") << "end" << false << 0 << true << false;
+ QTest::newRow("[weird]") << "weird" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::testKeywordGlobalSensitiveLocalInsensitive()
+{
+ m_definition->setKeywordsSensitive("true");
+ KeywordRule rule(m_definition);
+ rule.setInsensitive("true");
+ rule.setList("keywords");
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testKeywordGlobalSensitiveLocalInsensitive_data()
+{
+ addCommonColumns();
+
+ commonCasesForKeywords();
+ QTest::newRow("[fOr]") << "fOr" << true << 3 << false << false;
+ QTest::newRow("[whilE") << "whilE" << true << 5 << false << false;
+ QTest::newRow("[bEGIN]") << "bEGIN" << true << 5 << false << false;
+ QTest::newRow("[end]") << "end" << true << 3 << false << false;
+ QTest::newRow("[weird]") << "weird" << true << 5 << false << false;
+}
+
+void tst_SpecificRules::testKeywordGlobalInsensitiveLocalInsensitive()
+{
+ m_definition->setKeywordsSensitive("false");
+ KeywordRule rule(m_definition);
+ rule.setInsensitive("true");
+ rule.setList("keywords");
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testKeywordGlobalInsensitiveLocalInsensitive_data()
+{
+ testKeywordGlobalSensitiveLocalInsensitive_data();
+}
+
+void tst_SpecificRules::testKeywordGlobalInsensitiveLocalSensitive()
+{
+ m_definition->setKeywordsSensitive("false");
+ KeywordRule rule(m_definition);
+ rule.setInsensitive("false");
+ rule.setList("keywords");
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testKeywordGlobalInsensitiveLocalSensitive_data()
+{
+ testKeywordGlobalSensitiveLocalSensitive_data();
+}
+
+void tst_SpecificRules::noMatchForInt() const
+{
+ QTest::newRow("[1]") << "1" << false << 0 << true << false;
+ QTest::newRow("[1299]") << "1299" << false << 0 << true << false;
+ QTest::newRow("[10]") << "10" << false << 0 << true << false;
+ QTest::newRow("[9]") << "9" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::noMatchForFloat() const
+{
+ QTest::newRow("[4e-11]") << "4e-11" << false << 0 << true << false;
+ QTest::newRow("[1e+5]") << "1e+5" << false << 0 << true << false;
+ QTest::newRow("[7.321E-3]") << "7.321E-3" << false << 0 << true << false;
+ QTest::newRow("[3.2E+4]") << "3.2E+4" << false << 0 << true << false;
+ QTest::newRow("[0.5e-6]") << "0.5e-6" << false << 0 << true << false;
+ QTest::newRow("[0.45]") << "0.45" << false << 0 << true << false;
+ QTest::newRow("[6.e10]") << "6.e10" << false << 0 << true << false;
+ QTest::newRow("[.2e23]") << ".2e23" << false << 0 << true << false;
+ QTest::newRow("[23.]") << "23." << false << 0 << true << false;
+ QTest::newRow("[2.e23]") << "2.e23" << false << 0 << true << false;
+ QTest::newRow("[23e2]") << "23e2" << false << 0 << true << false;
+ QTest::newRow("[4.3e]") << "4.3e" << false << 0 << true << false;
+ QTest::newRow("[4.3ef]") << "4.3ef" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::noMatchForCOctal() const
+{
+ QTest::newRow("[0]") << "0" << false << 0 << true << false;
+ QTest::newRow("[07]") << "07" << false << 0 << true << false;
+ QTest::newRow("[01234567]") << "01234567" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::noMatchForCHex() const
+{
+ QTest::newRow("[0X934AF]") << "0X934AF" << false << 0 << true << false;
+ QTest::newRow("[0x934af]") << "0x934af" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::noMatchForNumber() const
+{
+ QTest::newRow("[a]") << "a" << false << 0 << true << false;
+ QTest::newRow("[#]") << "#" << false << 0 << true << false;
+ QTest::newRow("[ ]") << " " << false << 0 << true << false;
+ QTest::newRow("[a1]") << "a1" << false << 0 << true << false;
+ QTest::newRow("[.e23]") << ".e23" << false << 0 << true << false;
+ QTest::newRow("[.e23]") << ".e23" << false << 0 << true << false;
+
+ // + and - are not directly matched by number rules.
+ QTest::newRow("[+1]") << "+1" << false << 0 << true << false;
+ QTest::newRow("[-1]") << "-1" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::testInt()
+{
+ IntRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testInt_data()
+{
+ addCommonColumns();
+
+ noMatchForCOctal();
+ noMatchForCHex();
+ noMatchForNumber();
+
+ QTest::newRow("[1]") << "1" << true << 1 << false << false;
+ QTest::newRow("[1299]") << "1299" << true << 4 << false << false;
+ QTest::newRow("[10]") << "10" << true << 2 << false << false;
+ QTest::newRow("[9]") << "9" << true << 1 << false << false;
+
+ // LL, U, and others are matched through child rules.
+ QTest::newRow("[234U]") << "234U" << true << 3 << false << false;
+ QTest::newRow("[234LL]") << "234LL" << true << 3 << false << false;
+}
+
+void tst_SpecificRules::testFloat()
+{
+ FloatRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testFloat_data()
+{
+ addCommonColumns();
+
+ noMatchForInt();
+ noMatchForCOctal();
+ noMatchForCHex();
+ noMatchForNumber();
+
+ QTest::newRow("[4e-11]") << "4e-11" << true << 5 << false << false;
+ QTest::newRow("[1e+5]") << "1e+5" << true << 4 << false << false;
+ QTest::newRow("[7.321E-3]") << "7.321E-3" << true << 8 << false << false;
+ QTest::newRow("[3.2E+4]") << "3.2E+4" << true << 6 << false << false;
+ QTest::newRow("[0.5e-6]") << "0.5e-6" << true << 6 << false << false;
+ QTest::newRow("[0.45]") << "0.45" << true << 4 << false << false;
+ QTest::newRow("[6.e10]") << "6.e10" << true << 5 << false << false;
+ QTest::newRow("[.2e23]") << ".2e23" << true << 5 << false << false;
+ QTest::newRow("[23.]") << "23." << true << 3 << false << false;
+ QTest::newRow("[2.e23]") << "2.e23" << true << 5 << false << false;
+ QTest::newRow("[23e2]") << "23e2" << true << 4 << false << false;
+
+ // F, L, and others are matched through child rules.
+ QTest::newRow("[6.e10f]") << "6.e10f" << true << 5 << false << false;
+ QTest::newRow("[0.5e-6F]") << "0.5e-6F" << true << 6 << false << false;
+ QTest::newRow("[23.f]") << "23.f" << true << 3 << false << false;
+ QTest::newRow("[.2L]") << ".2L" << true << 2 << false << false;
+}
+
+void tst_SpecificRules::testCOctal()
+{
+ HlCOctRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testCOctal_data()
+{
+ addCommonColumns();
+
+ noMatchForInt();
+ noMatchForCHex();
+ noMatchForNumber();
+
+ QTest::newRow("[0]") << "0" << true << 1 << false << false;
+ QTest::newRow("[07]") << "07" << true << 2 << false << false;
+ QTest::newRow("[01234567]") << "01234567" << true << 8 << false << false;
+ QTest::newRow("[012345678]") << "012345678" << true << 8 << false << false;
+ QTest::newRow("[0888]") << "0888" << true << 1 << false << false;
+}
+
+void tst_SpecificRules::testCHex()
+{
+ HlCHexRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testCHex_data()
+{
+ addCommonColumns();
+
+ noMatchForInt();
+ noMatchForFloat();
+ noMatchForCOctal();
+ noMatchForNumber();
+
+ QTest::newRow("[0X934AF]") << "0X934AF" << true << 7 << false << false;
+ QTest::newRow("[0x934af]") << "0x934af" << true << 7 << false << false;
+ QTest::newRow("[0x2ik]") << "0x2ik" << true << 3 << false << false;
+}
+
+void tst_SpecificRules::testCString()
+{
+ HlCStringCharRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testCString_data()
+{
+ addCommonColumns();
+
+ // Escape sequences
+ QTest::newRow("[\\a]") << "\\a" << true << 2 << false << false;
+ QTest::newRow("[\\b]") << "\\b" << true << 2 << false << false;
+ QTest::newRow("[\\e]") << "\\e" << true << 2 << false << false;
+ QTest::newRow("[\\f]") << "\\f" << true << 2 << false << false;
+ QTest::newRow("[\\n]") << "\\n" << true << 2 << false << false;
+ QTest::newRow("[\\r]") << "\\r" << true << 2 << false << false;
+ QTest::newRow("[\\t]") << "\\t" << true << 2 << false << false;
+ QTest::newRow("[\\v]") << "\\v" << true << 2 << false << false;
+ QTest::newRow("[\\?]") << "\\?" << true << 2 << false << false;
+ QTest::newRow("[\\']") << "\\'" << true << 2 << false << false;
+ QTest::newRow("[\\\"]") << "\\\"" << true << 2 << false << false;
+ QTest::newRow("[\\\\]") << "\\\\" << true << 2 << false << false;
+ QTest::newRow("[\\c]") << "\\c" << false << 0 << true << false;
+ QTest::newRow("[x]") << "x" << false << 0 << true << false;
+ QTest::newRow("[ ]") << " " << false << 0 << true << false;
+ QTest::newRow("[a]") << "x" << false << 0 << true << false;
+ QTest::newRow("[r]") << "r" << false << 0 << true << false;
+ QTest::newRow("[//]") << "//" << false << 0 << true << false;
+
+ // Octal values
+ QTest::newRow("[\\1]") << "\\1" << true << 2 << false << false;
+ QTest::newRow("[\\12]") << "\\12" << true << 3 << false << false;
+ QTest::newRow("[\\123]") << "\\123" << true << 4 << false << false;
+ QTest::newRow("[\\1234]") << "\\1234" << true << 4 << false << false;
+ QTest::newRow("[\\123x]") << "\\123x" << true << 4 << false << false;
+
+ // Hex values
+ QTest::newRow("[\\xa]") << "\\xa" << true << 3 << false << false;
+ QTest::newRow("[\\xA]") << "\\xA" << true << 3 << false << false;
+ QTest::newRow("[\\Xa]") << "\\Xa" << false << 0 << true << false;
+ QTest::newRow("[\\xA10]") << "\\xA10" << true << 5 << false << false;
+ QTest::newRow("[\\xA0123456789]") << "\\xA0123456789" << true << 13 << false << false;
+ QTest::newRow("[\\xABCDEF]") << "\\xABCDEF" << true << 8 << false << false;
+ QTest::newRow("[\\xABCDEFGHI]") << "\\xABCDEFGHI" << true << 8 << false << false;
+}
+
+void tst_SpecificRules::testCChar()
+{
+ HlCCharRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testCChar_data()
+{
+ addCommonColumns();
+
+ // Escape sequences
+ QTest::newRow("[\'\\a\']") << "\'\\a\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\b\']") << "\'\\b\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\e\']") << "\'\\e\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\f\']") << "\'\\f\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\n\']") << "\'\\n\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\r\']") << "\'\\r\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\t\']") << "\'\\t\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\v\']") << "\'\\v\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\?\']") << "\'\\?\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\'\']") << "\'\\'\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\\"\']") << "\'\\\"\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\\\\']") << "\'\\\\\'" << true << 4 << false << false;
+ QTest::newRow("[\'\\c\']") << "\'\\c\'" << false << 0 << true << false;
+ QTest::newRow("[x]") << "x" << false << 0 << true << false;
+ QTest::newRow("[ ]") << " " << false << 0 << true << false;
+ QTest::newRow("[a]") << "x" << false << 0 << true << false;
+ QTest::newRow("[r]") << "r" << false << 0 << true << false;
+ QTest::newRow("[//]") << "//" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::testRangeDetect()
+{
+ QFETCH(QString, c);
+ QFETCH(QString, c1);
+ RangeDetectRule rule;
+ rule.setChar(c);
+ rule.setChar1(c1);
+
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testRangeDetect_data()
+{
+ QTest::addColumn<QString>("c");
+ QTest::addColumn<QString>("c1");
+ addCommonColumns();
+
+ QTest::newRow("[<>] against [<QString>]") << "<" << ">" << "<QString>"
+ << true << 9 << false << false;
+ QTest::newRow("[<>] against <x>") << "<" << ">" << "<x>" << true << 3 << false << false;
+ QTest::newRow("[<>] against < >") << "<" << ">" << "< >" << true << 5 << false << false;
+ QTest::newRow("[<>] against <x>abc") << "<" << ">" << "<x>abc" << true << 3 << false << false;
+ QTest::newRow("[<>] against <x> ") << "<" << ">" << "<x> " << true << 3 << false << false;
+ QTest::newRow("[<>] against abc") << "<" << ">" << "abc" << false << 0 << true << false;
+ QTest::newRow("[<>] against <abc") << "<" << ">" << "<abc" << false << 0 << true << false;
+ QTest::newRow("[<>] against abc<") << "<" << ">" << "abc<" << false << 0 << true << false;
+ QTest::newRow("[<>] against a<bc") << "<" << ">" << "a<bc" << false << 0 << true << false;
+ QTest::newRow("[<>] against abc<") << "<" << ">" << "abc<" << false << 0 << true << false;
+ QTest::newRow("[\"\"] against \"test.h\"") << "\"" << "\"" << "\"test.h\""
+ << true << 8 << false << false;
+}
+
+void tst_SpecificRules::testLineContinue()
+{
+ LineContinueRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testLineContinue_data()
+{
+ addCommonColumns();
+
+ QTest::newRow("\\") << "\\" << true << 0 << false << true;
+ QTest::newRow("\\\\") << "\\\\" << false << 0 << true << false;
+ QTest::newRow("\\x") << "\\x" << false << 0 << true << false;
+ QTest::newRow("x\\") << "x\\" << false << 0 << true << false;
+ QTest::newRow("x") << "x" << false << 0 << true << false;
+ QTest::newRow("/") << "/" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::testDetectSpaces()
+{
+ DetectSpacesRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testDetectSpaces_data()
+{
+ addCommonColumns();
+
+ QTest::newRow(" ") << " " << true << 1 << true << false;
+ QTest::newRow(" ") << " " << true << 4 << true << false;
+ QTest::newRow("\t") << "\t" << true << 1 << true << false;
+ QTest::newRow("x") << "x" << false << 0 << true << false;
+ QTest::newRow("#") << "#" << false << 0 << true << false;
+}
+
+void tst_SpecificRules::testDetectIdentifier()
+{
+ DetectIdentifierRule rule;
+ testMatch(rule);
+}
+
+void tst_SpecificRules::testDetectIdentifier_data()
+{
+ addCommonColumns();
+
+ QTest::newRow("name") << "name" << true << 4 << false << false;
+ QTest::newRow("x") << "x" << true << 1 << false << false;
+ QTest::newRow("x1") << "x1" << true << 2 << false << false;
+ QTest::newRow("1x") << "1x" << false << 0 << true << false;
+ QTest::newRow("_x") << "_x" << true << 2 << false << false;
+ QTest::newRow("___x") << "___x" << true << 4 << false << false;
+ QTest::newRow("-x") << "-x" << false << 0 << true << false;
+ QTest::newRow("@x") << "@x" << false << 0 << true << false;
+ QTest::newRow("+x") << "+x" << false << 0 << true << false;
+ QTest::newRow("#x") << "#x" << false << 0 << true << false;
+ QTest::newRow("x_x") << "x_x" << true << 3 << false << false;
+ QTest::newRow("x1x") << "x1x" << true << 3 << false << false;
+ QTest::newRow("x#x") << "x#x" << true << 1 << false << false;
+ QTest::newRow("x-x") << "x-x" << true << 1 << false << false;
+ QTest::newRow("abc_") << "abc_" << true << 4 << false << false;
+ QTest::newRow("abc____") << "abc____" << true << 7 << false << false;
+ QTest::newRow("abc-") << "abc-" << true << 3 << false << false;
+ QTest::newRow("abc$") << "abc$" << true << 3 << false << false;
+}
+
+QTEST_MAIN(tst_SpecificRules)
+#include "tst_specificrules.moc"
using namespace CPlusPlus;
-class ForEachNode: protected ASTVisitor
-{
- Document::Ptr doc;
- AST *pattern;
-
-public:
- ForEachNode(Document::Ptr doc)
- : ASTVisitor(doc->translationUnit()),
- matcher() {}
-
- void operator()() { accept(doc->translationUnit()->ast()); }
-
-protected:
- using ASTVisitor::visit;
-
- virtual bool preVisit(AST *ast)
- {
- ir.reset();
- IfStatementAST *pattern = ir.IfStatement(ir.SimpleName());
-
- //CompoundStatementAST *pattern = ir.CompoundStatement();
-
- if (ast->match(ast, pattern, &matcher))
- translationUnit()->warning(ast->firstToken(), "matched");
-
- return true;
- }
-
-
- ASTPatternBuilder ir;
- ASTMatcher matcher;
-};
-
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
doc->control()->setDiagnosticClient(0);
doc->setSource(source);
doc->parse();
-
- ForEachNode forEachNode(doc);
- forEachNode();
}
return EXIT_SUCCESS;
struct { int i; int b; };
struct { float f; };
double d;
- } a = { 42, 43 };
+ } a = { { 42, 43 } };
a.i = 1; // Break here. Expand a. Step.
a.i = 2; // Change a.i in Locals view to 0. This changes f, d but expectedly not b. Step.
a.i = 3; // Continue.
#endif
}
-void testFunctionPointer()
+typedef void (*func_t)();
+func_t testFunctionPointer()
{
- typedef void (*func_t)();
func_t f1 = testAnonymous;
- func_t f2 = testFunctionPointer;
- func_t f3 = testFunctionPointer;
+ func_t f2 = testPeekAndPoke3;
+ func_t f3 = testPeekAndPoke3;
Q_UNUSED(f1);
Q_UNUSED(f2);
Q_UNUSED(f3);
+ return f1;
}
-void testQByteArray()
+QByteArray testQByteArray()
{
QByteArray ba = "Hello";
ba += '"';
ba += char(0);
ba += 1;
ba += 2;
+ return ba;
}
static void throwit1()
return gotit;
}
-void testQDateTime()
+QDateTime testQDateTime()
{
QDateTime date;
date = QDateTime::currentDateTime();
date = date.addSecs(5);
date = date.addSecs(5);
date = date.addSecs(5);
+ return date;
}
-void testQFileInfo()
+QFileInfo testQFileInfo()
{
QFileInfo fi("/tmp/t");
QString s = fi.absoluteFilePath();
s = fi.bundleName();
s = fi.bundleName();
s = fi.bundleName();
+ return fi;
}
-void testQHash()
+QHash<int, float> testQHash()
{
#if 1
QHash<int, float> hgg0;
hash.insert("Welt", QPointer<QObject>(&ob));
hash.insert(".", QPointer<QObject>(&ob));
#endif
+ return hgg0;
}
void testQImage()
#endif
}
-void testStdList()
+std::list<int> testStdList()
{
std::list<int> big;
for (int i = 0; i < 10000; ++i)
vec.push_back(true);
vec.push_back(false);
#endif
+ return big;
}
void testStdMap()
#endif
}
-void testStdSet()
+std::set<int> testStdSet()
{
std::set<int> hgg0;
hgg0.insert(11);
std::set<QPointer<QObject> > hash;
QPointer<QObject> ptr(&ob);
#endif
+ return hgg0;
}
-void testStdStack()
+std::stack<int> testStdStack()
{
std::stack<int *> plist1;
plist1.push(new int(1));
std::stack<Foo> flist;
flist.push(1);
flist.push(2);
+
+ return flist2;
}
-void testStdString()
+std::string testStdString()
{
QString foo;
std::string str;
v.push_back(str);
v.push_back(str);
v.push_back(str);
+
+ return str;
}
-void testStdVector()
+std::vector<int> testStdVector()
{
std::vector<int *> plist1;
plist1.push_back(new int(1));
std::vector<bool> vec;
vec.push_back(true);
vec.push_back(false);
+
+ return flist2;
}
void testQStandardItemModel()
++i;
}
-void testQStack()
+QStack<int> testQStack()
{
QVector<int> bigv;
for (int i = 0; i < 10; ++i)
QStack<bool> vec;
vec.append(true);
vec.append(false);
+ return big;
}
-void testQString()
+QString testQString()
{
QUrl url(QString("http://www.nokia.com"));
str += " World ";
str += " World ";
str += " World ";
+ return str;
}
void testQString3()
delete pstring;
}
-void testQStringList()
+QStringList testQStringList()
{
QStringList l;
l << "Hello ";
l << " fat ";
l.takeFirst();
l << " World ";
+ return l;
}
-void testStruct()
+Foo testStruct()
{
Foo f(2);
f.doit();
f.doit();
f.doit();
+ return f;
}
class Thread : public QThread
void run()
{
+ int j = 2;
+ ++j;
for (int i = 0; i != 100000; ++i) {
//sleep(1);
std::cerr << m_id;
}
+ std::cerr << j;
}
private:
v = 1.0;
v = "string";
v = QRect(100, 200, 300, 400);
+ v = QRectF(100, 200, 300, 400);
v = 1;
+ //return v;
}
-void testQVariant2()
+QVariant testQVariant2()
{
QVariant value;
QVariant::Type t = QVariant::String;
var.setValue(my);
var.setValue(my);
#endif
+ return QVariant("sss");
}
-void testQVariant3()
+QVariant testQVariant3()
{
+ QVariantList vl;
+ vl.append(QVariant(1));
+ vl.append(QVariant(2));
+ vl.append(QVariant("Some String"));
+ vl.append(QVariant(21));
+ vl.append(QVariant(22));
+ vl.append(QVariant("2Some String"));
+
QList<int> list;
list << 1 << 2 << 3;
QVariant variant = qVariantFromValue(list);
list.clear();
list = qVariantValue<QList<int> >(variant);
+ return QVariant("xxx");
}
-void testQVector()
+QVector<int> testQVector()
{
QVector<int> big(10000);
QVector<bool> vec;
vec.append(true);
vec.append(false);
+
+ return big;
}
-void testQVectorOfQList()
+QVector<QList<int> > testQVectorOfQList()
{
QVector<QList<int> > v;
QVector<QList<int> > *pv = &v;
v.append(QList<int>() << 1);
v.append(QList<int>() << 2 << 3);
Q_UNUSED(pv);
+ return v;
}
void testPointer()
{
Foo *f = new Foo();
+ Q_UNUSED(f);
int i = 0;
++i;
++i;
return a.size() + b.size() + c.size();
}
+struct Color
+{
+ int r,g,b,a;
+ Color() { r = 1, g = 2, b = 3, a = 4; }
+};
+
+Color testColor()
+{
+ Color c;
+ c.r = 5;
+ return c;
+}
+
+int fooii()
+{
+ return 3;
+}
+
+void testStuff()
+{
+ int i = 0;
+ int *p = &i;
+ ++*p;
+}
+
int main(int argc, char *argv[])
{
+ testColor();
+ testStuff();
testPeekAndPoke3();
testFunctionPointer();
testAnonymous();
testPlugin();
testQList();
testQLinkedList();
- char *s = "aöa";
- wchar_t *w = L"aöa";
+ const char *s = "aöa";
+ const wchar_t *w = L"aöa";
testNamespace();
//return 0;
testQHash();
#endif
testQStringList();
testStruct();
- //testThreads();
+ testQThread();
testQVariant1();
testQVariant2();
testQVariant3();