diff --git a/qtcreator.qbs b/qtcreator.qbs index a56dc16a8edb455418257e7d0cec8bb633ab0018..159c1b52d5b98fdcdce88733efb498d694814e6e 100644 --- a/qtcreator.qbs +++ b/qtcreator.qbs @@ -60,6 +60,7 @@ Project { "src/plugins/perforce/perforce.qbs", "src/plugins/projectexplorer/projectexplorer.qbs", "src/plugins/qbsprojectmanager/qbsprojectmanager.qbs", + "src/plugins/pythoneditor/pythoneditor.qbs", // "src/plugins/qmldesigner/qmldesigner.qbs", "src/plugins/qmljseditor/qmljseditor.qbs", "src/plugins/qmljstools/qmljstools.qbs", diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 1cdbf275789c2502f8014ea870617e38132df26c..f39b79c5c5dffc51f085cd2f4ae7e24a21331dda 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -32,6 +32,7 @@ SUBDIRS = plugin_coreplugin \ plugin_genericprojectmanager \ plugin_qmljseditor \ plugin_glsleditor \ + plugin_pythoneditor \ plugin_mercurial \ plugin_bazaar \ plugin_classview \ @@ -350,3 +351,8 @@ plugin_clearcase.subdir = clearcase plugin_clearcase.depends = plugin_vcsbase plugin_clearcase.depends += plugin_projectexplorer plugin_clearcase.depends += plugin_coreplugin + +plugin_pythoneditor.subdir = pythoneditor +plugin_pythoneditor.depends = plugin_coreplugin +plugin_pythoneditor.depends += plugin_cpptools +plugin_pythoneditor.depends += plugin_texteditor diff --git a/src/plugins/pythoneditor/PythonEditor.pluginspec.in b/src/plugins/pythoneditor/PythonEditor.pluginspec.in new file mode 100644 index 0000000000000000000000000000000000000000..a8cc126001f9dfa3cbff760724d763f6ddb75918 --- /dev/null +++ b/src/plugins/pythoneditor/PythonEditor.pluginspec.in @@ -0,0 +1,22 @@ +<plugin name=\"PythonEditor\" version=\"$$QTCREATOR_VERSION\" compatVersion=\"$$QTCREATOR_VERSION\"> + <vendor>Digia Plc</vendor> + <copyright>(C) 2013 Digia Plc</copyright> + <license> +Commercial Usage + +Licensees holding valid Qt Commercial licenses may use this plugin 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 Digia. + +GNU Lesser General Public License Usage + +Alternatively, this plugin may be used under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. 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. + </license> + <category>Python</category> + <description>Editor and file creation wizards for Python. Example plugin for QtCreator API demonstration.</description> + <url>http://www.qt-project.org</url> + <dependencyList> + <dependency name=\"Core\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"TextEditor\" version=\"$$QTCREATOR_VERSION\"/> + <dependency name=\"CppTools\" version=\"$$QTCREATOR_VERSION\"/> + </dependencyList> +</plugin> + diff --git a/src/plugins/pythoneditor/pythoneditor.cpp b/src/plugins/pythoneditor/pythoneditor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7704e2764ced996843ef20b359f5be7c58da196d --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditor.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +/** + \class PyEditor::Editor implements interface Core::IEditor + This editor makes possible to edit Python source files + */ + +#include "pythoneditorconstants.h" +#include "pythoneditorplugin.h" +#include "pythoneditorwidget.h" +#include "pythoneditor.h" + +#include <coreplugin/icore.h> +#include <coreplugin/mimedatabase.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/texteditorsettings.h> + +#include <QFileInfo> + +namespace PythonEditor { + +PythonEditor::PythonEditor(EditorWidget *editorWidget) + :BaseTextEditor(editorWidget) +{ + setContext(Core::Context(Constants::C_PYTHONEDITOR_ID, + TextEditor::Constants::C_TEXTEDITOR)); +} + +PythonEditor::~PythonEditor() +{ +} + +Core::IEditor *PythonEditor::duplicate(QWidget *parent) +{ + EditorWidget *widget = new EditorWidget(parent); + widget->duplicateFrom(editorWidget()); + PythonEditorPlugin::initializeEditor(widget); + + return widget->editor(); +} + +/** + * @returns Unique editor class identifier, that is Constants::C_PYTHONEDITOR_ID + */ +Core::Id PythonEditor::id() const +{ + return Core::Id(Constants::C_PYTHONEDITOR_ID); +} + +Core::Id PythonEditor::preferredModeType() const +{ + return Core::Id(); +} + +bool PythonEditor::open(QString *errorString, + const QString &fileName, + const QString &realFileName) +{ + Core::MimeType mimeType; + Core::MimeDatabase *mimeDB = Core::ICore::instance()->mimeDatabase(); + + mimeType = mimeDB->findByFile(QFileInfo(fileName)); + editorWidget()->setMimeType(mimeType.type()); + + bool status = TextEditor::BaseTextEditor::open(errorString, + fileName, + realFileName); + return status; +} + +} // namespace PythonEditor diff --git a/src/plugins/pythoneditor/pythoneditor.h b/src/plugins/pythoneditor/pythoneditor.h new file mode 100644 index 0000000000000000000000000000000000000000..1ca117458fb74ebaba552403ea72c441af3d72da --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditor.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_EDITOR_H +#define PYTHONEDITOR_EDITOR_H + +#include "pythoneditor_global.h" +#include <texteditor/basetexteditor.h> + +namespace PythonEditor { + +class EditorWidget; + +class PYEDITOR_EXPORT PythonEditor : public TextEditor::BaseTextEditor +{ + Q_OBJECT + +public: + explicit PythonEditor(EditorWidget *editorWidget); + virtual ~PythonEditor(); + + bool duplicateSupported() const { return true; } + Core::IEditor *duplicate(QWidget *parent); + + Core::Id id() const; + bool isTemporary() const { return false; } + Core::Id preferredModeType() const; + + /** + Opens file for editing, actual work performed by base class + */ + bool open(QString *errorString, + const QString &fileName, + const QString &realFileName); +}; + +} // namespace PythonEditor + +#endif // PYTHONEDITOR_EDITOR_H diff --git a/src/plugins/pythoneditor/pythoneditor.mimetypes.xml b/src/plugins/pythoneditor/pythoneditor.mimetypes.xml new file mode 100644 index 0000000000000000000000000000000000000000..61009c0d0ec5906a432fef503367a95799c0b24c --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditor.mimetypes.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'> + <mime-type type="text/x-python"> + <sub-class-of type="text/plain"/> + <comment>Python Source File</comment> + <glob pattern="*.py"/> + <glob pattern="*.pyw"/> + <glob pattern="*.wsgi"/> + </mime-type> +</mime-info> diff --git a/src/plugins/pythoneditor/pythoneditor.pro b/src/plugins/pythoneditor/pythoneditor.pro new file mode 100644 index 0000000000000000000000000000000000000000..595f942f9c80cabb2d6460122db0e6b6b441d27e --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditor.pro @@ -0,0 +1,42 @@ +TEMPLATE = lib +TARGET = PythonEditor + +include(../../qtcreatorplugin.pri) + +# dependencies +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/cpptools/cpptools.pri) + +DEFINES += \ + PYEDITOR_LIBRARY + +OTHER_FILES += PythonEditor.pluginspec.in \ + pythoneditor.mimetypes.xml + +RESOURCES += \ + pythoneditorplugin.qrc + +HEADERS += \ + pythoneditor_global.h \ + pythoneditorplugin.h \ + pythoneditorfactory.h \ + pythoneditor.h \ + pythoneditorwidget.h \ + pythoneditorconstants.h \ + wizard/pythonfilewizard.h \ + tools/pythonhighlighter.h \ + tools/pythonindenter.h \ + tools/lexical/pythonformattoken.h \ + tools/lexical/pythonscanner.h \ + tools/lexical/sourcecodestream.h + +SOURCES += \ + pythoneditorplugin.cpp \ + pythoneditorfactory.cpp \ + pythoneditor.cpp \ + pythoneditorwidget.cpp \ + wizard/pythonfilewizard.cpp \ + tools/pythonhighlighter.cpp \ + tools/pythonindenter.cpp \ + tools/lexical/pythonscanner.cpp diff --git a/src/plugins/pythoneditor/pythoneditor.qbs b/src/plugins/pythoneditor/pythoneditor.qbs new file mode 100644 index 0000000000000000000000000000000000000000..e9beb6ba95f50b583358ecca0764959a6f53dcdc --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditor.qbs @@ -0,0 +1,41 @@ +import qbs.base 1.0 + +import "../QtcPlugin.qbs" as QtcPlugin + +QtcPlugin { + name: "PythonEditor" + + Depends { name: "Core" } + Depends { name: "TextEditor" } + Depends { name: "CppTools" } + Depends { name: "cpp" } + + cpp.defines: base.concat(["PYEDITOR_LIBRARY", "QT_CREATOR"]) + + files: [ + "pythoneditor.cpp", + "pythoneditor.h", + "pythoneditor.mimetypes.xml", + "pythoneditor_global.h", + "pythoneditorconstants.h", + "pythoneditorfactory.cpp", + "pythoneditorfactory.h", + "pythoneditorfeatures.h", + "pythoneditorplugin.cpp", + "pythoneditorplugin.h", + "pythoneditorplugin.qrc", + "pythoneditorwidget.cpp", + "pythoneditorwidget.h", + "tools/lexical/sourcecodestream.h", + "tools/lexical/pythonscanner.h", + "tools/lexical/pythonformattoken.h", + "tools/lexical/pythonscanner.cpp", + "tools/pythonhighlighter.h", + "tools/pythonindenter.cpp", + "tools/pythonhighlighter.cpp", + "tools/pythonindenter.h", + "wizard/pythonfilewizard.h", + "wizard/pythonfilewizard.cpp", + ] +} + diff --git a/src/plugins/pythoneditor/pythoneditor_global.h b/src/plugins/pythoneditor/pythoneditor_global.h new file mode 100644 index 0000000000000000000000000000000000000000..83b810d75b1f4984ee57667170048f15f59bdca0 --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditor_global.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_GLOBAL_H +#define PYTHONEDITOR_GLOBAL_H + +#include <QtGlobal> + +#if defined(PYEDITOR_LIBRARY) +# define PYEDITOR_EXPORT Q_DECL_EXPORT +#else +# define PYEDITOR_EXPORT Q_DECL_IMPORT +#endif + +#endif // PYTHONEDITOR_GLOBAL_H diff --git a/src/plugins/pythoneditor/pythoneditorconstants.h b/src/plugins/pythoneditor/pythoneditorconstants.h new file mode 100644 index 0000000000000000000000000000000000000000..48b9a1b4130620ba3a2842c74c838f94ae914adf --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorconstants.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_CONSTANTS_H +#define PYTHONEDITOR_CONSTANTS_H + +#include <QtGlobal> + +namespace PythonEditor { +namespace Constants { + +const char C_PYTHONEDITOR_ID[] = "PythonEditor.PythonEditor"; +const char C_EDITOR_DISPLAY_NAME[] = + QT_TRANSLATE_NOOP("OpenWith::Editors", "Python Editor"); + +/******************************************************************************* + * File creation wizard + ******************************************************************************/ +const char C_PY_WIZARD_CATEGORY[] = "U.Python"; +const char C_PY_EXTENSION[] = ".py"; +const char C_PY_DISPLAY_CATEGORY[] = "Python"; + + // source +const char C_PY_SOURCE_WIZARD_ID[] = "P.PySource"; +const char C_PY_SOURCE_CONTENT[] = + "#!/usr/bin/env python\n" + "# -*- coding: utf-8 -*-\n" + "\n"; +const char EN_PY_SOURCE_DISPLAY_NAME[] = "Python source file"; +const char EN_PY_SOURCE_DESCRIPTION[] = + "Creates an empty python script with utf-8 charset"; + + // class +const char C_PY_CLASS_WIZARD_ID[] = "P.PyClass"; +const char EN_PY_CLASS_DISPLAY_NAME[] = "Python class"; +const char EN_PY_CLASS_DESCRIPTION[] = + "Creates new Python class"; + + // For future: boost binding +const char C_PY_CPPMODULE_WIZARD_ID[] = "F.PyCppModule"; +const char EN_PY_CPPMODULE_DISPLAY_NAME[] = "C++ module for Python"; +const char EN_PY_CPPMODULE_DESCRIPTION[] = + "Creates C++/boost file with bindings for python"; + +/******************************************************************************* + * MIME type + ******************************************************************************/ +const char C_PY_MIMETYPE[] = "text/x-python"; +const char RC_PY_MIME_XML[] = ":/pythoneditor/pythoneditor.mimetypes.xml"; +const char C_PY_MIME_ICON[] = "text-x-python"; + +} // namespace Constants +} // namespace PythonEditor + +#endif // PYTHONEDITOR_CONSTANTS_H diff --git a/src/plugins/pythoneditor/pythoneditorfactory.cpp b/src/plugins/pythoneditor/pythoneditorfactory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23b6a5944719d885b5f644180cef237bce41b2eb --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorfactory.cpp @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "pythoneditorconstants.h" +#include "pythoneditorwidget.h" +#include "pythoneditorplugin.h" +#include "pythoneditorfactory.h" + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <texteditor/texteditorsettings.h> + +#include <QDebug> + +namespace PythonEditor { + +EditorFactory::EditorFactory(QObject *parent) + : Core::IEditorFactory(parent) +{ + m_mimeTypes << QLatin1String(Constants::C_PY_MIMETYPE); +} + +Core::Id EditorFactory::id() const +{ + return Constants::C_PYTHONEDITOR_ID; +} + +QString EditorFactory::displayName() const +{ + return tr(Constants::C_EDITOR_DISPLAY_NAME); +} + +Core::IDocument *EditorFactory::open(const QString &fileName) +{ + Core::IEditor *iface = Core::EditorManager::instance()->openEditor(fileName, id()); + if (!iface) { + qWarning() << "CEditorFactory::open: openEditor failed for " << fileName; + return 0; + } + return iface->document(); +} + +Core::IEditor *EditorFactory::createEditor(QWidget *parent) +{ + EditorWidget *widget = new EditorWidget(parent); + PythonEditorPlugin::initializeEditor(widget); + + return widget->editor(); +} + +QStringList EditorFactory::mimeTypes() const +{ + return m_mimeTypes; +} + +} // namespace PythonEditor diff --git a/src/plugins/pythoneditor/pythoneditorfactory.h b/src/plugins/pythoneditor/pythoneditorfactory.h new file mode 100644 index 0000000000000000000000000000000000000000..b53ca3d2315df9774edfa1807ae196b06c409951 --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorfactory.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_EDITORFACTORY_H +#define PYTHONEDITOR_EDITORFACTORY_H + +#include "pythoneditor_global.h" +#include <coreplugin/editormanager/ieditorfactory.h> +#include <QStringList> + +namespace PythonEditor { + +class PYEDITOR_EXPORT EditorFactory : public Core::IEditorFactory +{ + Q_OBJECT + +public: + EditorFactory(QObject *parent); + + /** + Returns MIME types handled by editor + */ + QStringList mimeTypes() const; + + /** + Unique editor class identifier, see Constants::C_PYEDITOR_ID + */ + Core::Id id() const; + QString displayName() const; + + /** + Opens file in new editor + */ + Core::IDocument *open(const QString &fileName); + + /** + Creates and initializes new editor widget + */ + Core::IEditor *createEditor(QWidget *parent); + +private: + QStringList m_mimeTypes; +}; + +} // namespace PythonEditor + +#endif // PYTHONEDITOR_EDITORFACTORY_H diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..893fac076de9e7c2d8cba28aa6d417896fe23e79 --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "pythoneditorplugin.h" +#include "pythoneditorconstants.h" +#include "wizard/pythonfilewizard.h" +#include "pythoneditorwidget.h" +#include "pythoneditorfactory.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/mimedatabase.h> +#include <coreplugin/fileiconprovider.h> +#include <coreplugin/id.h> +#include <coreplugin/editormanager/editormanager.h> +#include <extensionsystem/pluginmanager.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/texteditorsettings.h> + +#include <QtPlugin> +#include <QCoreApplication> + +using namespace PythonEditor::Constants; + +/******************************************************************************* + * List of Python keywords (includes "print" that isn't keyword in python 3 + ******************************************************************************/ +static const char *const LIST_OF_PYTHON_KEYWORDS[] = { + "and", + "as", + "assert", + "break", + "class", + "continue", + "def", + "del", + "elif", + "else", + "except", + "exec", + "finally", + "for", + "from", + "global", + "if", + "import", + "in", + "is", + "lambda", + "not", + "or", + "pass", + "print", + "raise", + "return", + "try", + "while", + "with", + "yield" +}; + +/******************************************************************************* + * List of Python magic methods and attributes + ******************************************************************************/ +static const char *const LIST_OF_PYTHON_MAGICS[] = { + // ctor & dtor + "__init__", + "__del__", + // string conversion functions + "__str__", + "__repr__", + "__unicode__", + // attribute access functions + "__setattr__", + "__getattr__", + "__delattr__", + // binary operators + "__add__", + "__sub__", + "__mul__", + "__truediv__", + "__floordiv__", + "__mod__", + "__pow__", + "__and__", + "__or__", + "__xor__", + "__eq__", + "__ne__", + "__gt__", + "__lt__", + "__ge__", + "__le__", + "__lshift__", + "__rshift__", + "__contains__", + // unary operators + "__pos__", + "__neg__", + "__inv__", + "__abs__", + "__len__", + // item operators like [] + "__getitem__", + "__setitem__", + "__delitem__", + "__getslice__", + "__setslice__", + "__delslice__", + // other functions + "__cmp__", + "__hash__", + "__nonzero__", + "__call__", + "__iter__", + "__reversed__", + "__divmod__", + "__int__", + "__long__", + "__float__", + "__complex__", + "__hex__", + "__oct__", + "__index__", + "__copy__", + "__deepcopy__", + "__sizeof__", + "__trunc__", + "__format__", + // magic attributes + "__name__", + "__module__", + "__dict__", + "__bases__", + "__doc__" +}; + +/******************************************************************************* + * List of python built-in functions and objects + ******************************************************************************/ +static const char *const LIST_OF_PYTHON_BUILTINS[] = { + "range", + "xrange", + "int", + "float", + "long", + "hex", + "oct" + "chr", + "ord", + "len", + "abs", + "None", + "True", + "False" +}; + +namespace PythonEditor { + +PythonEditorPlugin *PythonEditorPlugin::m_instance = 0; + +/// Copies identifiers from array to QSet +static void copyIdentifiers(const char * const words[], size_t bytesCount, QSet<QString> &result) +{ + const size_t count = bytesCount / sizeof(const char * const); + for (size_t i = 0; i < count; ++i) + result.insert(QLatin1String(words[i])); +} + +PythonEditorPlugin::PythonEditorPlugin() + : m_factory(0) + , m_actionHandler(0) +{ + m_instance = this; + copyIdentifiers(LIST_OF_PYTHON_KEYWORDS, sizeof(LIST_OF_PYTHON_KEYWORDS), m_keywords); + copyIdentifiers(LIST_OF_PYTHON_MAGICS, sizeof(LIST_OF_PYTHON_MAGICS), m_magics); + copyIdentifiers(LIST_OF_PYTHON_BUILTINS, sizeof(LIST_OF_PYTHON_BUILTINS), m_builtins); +} + +PythonEditorPlugin::~PythonEditorPlugin() +{ + removeObject(m_factory); + m_instance = 0; +} + +bool PythonEditorPlugin::initialize( + const QStringList &arguments, QString *errorMessage) +{ + Q_UNUSED(arguments) + + Core::ICore *pCore = Core::ICore::instance(); + + if (! pCore->mimeDatabase()->addMimeTypes( + QLatin1String(RC_PY_MIME_XML), + errorMessage)) + { + return false; + } + + m_factory = new EditorFactory(this); + addObject(m_factory); + + //////////////////////////////////////////////////////////////////////////// + // Initialize editor actions handler + //////////////////////////////////////////////////////////////////////////// + m_actionHandler.reset(new TextEditor::TextEditorActionHandler( + C_PYTHONEDITOR_ID, + TextEditor::TextEditorActionHandler::Format + | TextEditor::TextEditorActionHandler::UnCommentSelection + | TextEditor::TextEditorActionHandler::UnCollapseAll)); + m_actionHandler->initializeActions(); + + //////////////////////////////////////////////////////////////////////////// + // Add MIME overlay icons (these icons displayed at Project dock panel) + //////////////////////////////////////////////////////////////////////////// + const QIcon icon = QIcon::fromTheme(QLatin1String(C_PY_MIME_ICON)); + if (!icon.isNull()) { + Core::FileIconProvider *iconProv = Core::FileIconProvider::instance(); + Core::MimeDatabase *mimeDB = Core::ICore::instance()->mimeDatabase(); + iconProv->registerIconOverlayForMimeType( + icon, mimeDB->findByType(QLatin1String(C_PY_MIMETYPE))); + } + + //////////////////////////////////////////////////////////////////////////// + // Add Python files and classes creation dialogs + //////////////////////////////////////////////////////////////////////////// + addAutoReleasedObject(new FileWizard(Core::ICore::instance())); + + return true; +} + +void PythonEditorPlugin::extensionsInitialized() +{ +} + +void PythonEditorPlugin::initializeEditor(EditorWidget *widget) +{ + instance()->m_actionHandler->setupActions(widget); + TextEditor::TextEditorSettings::instance()->initializeEditor(widget); +} + +QSet<QString> PythonEditorPlugin::keywords() +{ + return instance()->m_keywords; +} + +QSet<QString> PythonEditorPlugin::magics() +{ + return instance()->m_magics; +} + +QSet<QString> PythonEditorPlugin::builtins() +{ + return instance()->m_builtins; +} + +} // namespace PythonEditor + +Q_EXPORT_PLUGIN(PythonEditor::PythonEditorPlugin) + diff --git a/src/plugins/pythoneditor/pythoneditorplugin.h b/src/plugins/pythoneditor/pythoneditorplugin.h new file mode 100644 index 0000000000000000000000000000000000000000..093536dddbcd886cc12c3470b7fe1526391d39df --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorplugin.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_PLUGIN_H +#define PYTHONEDITOR_PLUGIN_H + +#include <extensionsystem/iplugin.h> +#include <texteditor/texteditoractionhandler.h> +#include <QSet> +#include <QScopedPointer> + +namespace PythonEditor { + +class EditorFactory; +class EditorWidget; + +/** + \class PyEditor::Plugin implements ExtensionSystem::IPlugin + Singletone object - PyEditor plugin + */ +class PythonEditorPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "PythonEditor.json") + +public: + PythonEditorPlugin(); + virtual ~PythonEditorPlugin(); + + virtual bool initialize(const QStringList &arguments, QString *errorMessage); + virtual void extensionsInitialized(); + static PythonEditorPlugin *instance() { return m_instance; } + static void initializeEditor(EditorWidget *widget); + + static QSet<QString> keywords(); + static QSet<QString> magics(); + static QSet<QString> builtins(); + +private: + static PythonEditorPlugin *m_instance; + EditorFactory *m_factory; + QScopedPointer<TextEditor::TextEditorActionHandler> m_actionHandler; + QSet<QString> m_keywords; + QSet<QString> m_magics; + QSet<QString> m_builtins; +}; + +} // namespace PythonEditor + +#endif // PYTHONEDITOR_PLUGIN_H diff --git a/src/plugins/pythoneditor/pythoneditorplugin.qrc b/src/plugins/pythoneditor/pythoneditorplugin.qrc new file mode 100644 index 0000000000000000000000000000000000000000..47440b45d95abe295eade3e3a07d3009451b4981 --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorplugin.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/pythoneditor"> + <file>pythoneditor.mimetypes.xml</file> + </qresource> +</RCC> diff --git a/src/plugins/pythoneditor/pythoneditorwidget.cpp b/src/plugins/pythoneditor/pythoneditorwidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d0832082037f054f82681a9320b04f7f621ada9 --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorwidget.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +/** + \class EditorWidget + Graphical representation and parent widget for PyEditor::Editor class. + Represents editor as plain text editor. + */ + +#include "tools/pythonhighlighter.h" +#include "tools/pythonindenter.h" +#include "pythoneditor.h" +#include "pythoneditorwidget.h" + +#include <texteditor/fontsettings.h> +#include <texteditor/texteditorconstants.h> +#include <texteditor/basetextdocument.h> +#include <texteditor/indenter.h> +#include <texteditor/autocompleter.h> + +namespace PythonEditor { + +EditorWidget::EditorWidget(QWidget *parent) + :TextEditor::BaseTextEditorWidget(parent) +{ + m_commentDefinition.setMultiLineStart(QString()); + m_commentDefinition.setMultiLineEnd(QString()); + m_commentDefinition.setSingleLine(QLatin1String("#")); + + setParenthesesMatchingEnabled(true); + setMarksVisible(true); + setCodeFoldingSupported(true); + + setIndenter(new PythonIndenter()); + new PythonHighlighter(baseTextDocument().data()); +} + +EditorWidget::~EditorWidget() +{ +} + +/** + Comments or uncomments selection using Python commenting syntax + */ +void EditorWidget::unCommentSelection() +{ + Utils::unCommentSelection(this, m_commentDefinition); +} + +/** + Handles common IDE fonts&colors settings + (Tools -> Options -> Text editor -> Fonts and colors) + */ +void EditorWidget::setFontSettings(const TextEditor::FontSettings &fs) +{ + TextEditor::BaseTextEditorWidget::setFontSettings(fs); + +#ifdef PYTHONEDITOR_HIGHLIGHTER_H + PythonHighlighter *highlighter = + qobject_cast<PythonHighlighter *>(baseTextDocument()->syntaxHighlighter()); + if (highlighter) + { + highlighter->setFontSettings(fs); + } +#endif +} + +TextEditor::BaseTextEditor *EditorWidget::createEditor() +{ + return new PythonEditor(this); +} + +} // namespace PythonEditor diff --git a/src/plugins/pythoneditor/pythoneditorwidget.h b/src/plugins/pythoneditor/pythoneditorwidget.h new file mode 100644 index 0000000000000000000000000000000000000000..9d016850d79a24ce06cb87dcd874d709e307da49 --- /dev/null +++ b/src/plugins/pythoneditor/pythoneditorwidget.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_EDITORWIDGET_H +#define PYTHONEDITOR_EDITORWIDGET_H + +#include "pythoneditor_global.h" + +#include <texteditor/basetexteditor.h> +#include <utils/uncommentselection.h> + +namespace PythonEditor { + +class PYEDITOR_EXPORT EditorWidget : public TextEditor::BaseTextEditorWidget +{ + Q_OBJECT + +public: + EditorWidget(QWidget *parent = 0); + virtual ~EditorWidget(); + + virtual void unCommentSelection(); + +public slots: + virtual void setFontSettings(const TextEditor::FontSettings &); + +protected: + TextEditor::BaseTextEditor *createEditor(); + +private: + Utils::CommentDefinition m_commentDefinition; +}; + +} // namespace PythonEditor + +#endif // PYTHONEDITOR_EDITORWIDGET_H diff --git a/src/plugins/pythoneditor/tools/lexical/pythonformattoken.h b/src/plugins/pythoneditor/tools/lexical/pythonformattoken.h new file mode 100644 index 0000000000000000000000000000000000000000..5f6e5f7a5c31fec5f0786362b24d6eb9c3d050cf --- /dev/null +++ b/src/plugins/pythoneditor/tools/lexical/pythonformattoken.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_FORMATTOKEN_H +#define PYTHONEDITOR_FORMATTOKEN_H + +#include <stdlib.h> + +namespace PythonEditor { +namespace Internal { + +enum Format { + Format_Number = 0, + Format_String, + Format_Keyword, + Format_Type, + Format_ClassField, + Format_MagicAttr, // magic class attribute/method, like __name__, __init__ + Format_Operator, + Format_Comment, + Format_Doxygen, + Format_Identifier, + Format_Whitespace, + Format_ImportedModule, + + Format_FormatsAmount, + Format_EndOfBlock +}; + +class FormatToken +{ +public: + FormatToken() {} + + FormatToken(Format format, size_t position, size_t length) + :m_format(format) + ,m_position(position) + ,m_length(length) + {} + + inline Format format() const { return m_format; } + inline int begin() const { return m_position; } + inline int end() const { return m_position + m_length; } + inline int length() const { return m_length; } + +private: + Format m_format; + int m_position; + int m_length; +}; + +} // namespace Internal +} // namespace PythonEditor + +#endif // PYTHONEDITOR_FORMATTOKEN_H diff --git a/src/plugins/pythoneditor/tools/lexical/pythonscanner.cpp b/src/plugins/pythoneditor/tools/lexical/pythonscanner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..92fb7b13ede566f1dd0f3833f42168b209ac4b14 --- /dev/null +++ b/src/plugins/pythoneditor/tools/lexical/pythonscanner.cpp @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "../../pythoneditorconstants.h" +#include "../../pythoneditorplugin.h" +#include "pythonscanner.h" + +#include <QString> +#include <QSet> + +namespace PythonEditor { +namespace Internal { + +Scanner::Scanner(const QChar *text, const int length) + : m_src(text, length) + , m_state(0) + , m_keywords(PythonEditorPlugin::keywords()) + , m_magics(PythonEditorPlugin::magics()) + , m_builtins(PythonEditorPlugin::builtins()) +{ +} + +Scanner::~Scanner() +{ +} + +void Scanner::setState(int state) +{ + m_state = state; +} + +int Scanner::state() const +{ + return m_state; +} + +FormatToken Scanner::read() +{ + m_src.setAnchor(); + if (m_src.isEnd()) + return FormatToken(Format_EndOfBlock, m_src.anchor(), 0); + + State state; + QChar saved; + parseState(state, saved); + switch (state) { + case State_String: + return readStringLiteral(saved); + case State_MultiLineString: + return readMultiLineStringLiteral(saved); + default: + return onDefaultState(); + } +} + +QString Scanner::value(const FormatToken &tk) const +{ + return m_src.value(tk.begin(), tk.length()); +} + +FormatToken Scanner::onDefaultState() +{ + QChar first = m_src.peek(); + m_src.move(); + + if (first == QLatin1Char('\\') && m_src.peek() == QLatin1Char('\n')) { + m_src.move(); + return FormatToken(Format_Whitespace, m_src.anchor(), 2); + } + + if (first == QLatin1Char('.') && m_src.peek().isDigit()) + return readFloatNumber(); + + if (first == QLatin1Char('\'') || first == QLatin1Char('\"')) + return readStringLiteral(first); + + if (first.isLetter() || (first == QLatin1Char('_'))) + return readIdentifier(); + + if (first.isDigit()) + return readNumber(); + + if (first == QLatin1Char('#')) { + if (m_src.peek() == QLatin1Char('#')) + return readDoxygenComment(); + return readComment(); + } + + if (first.isSpace()) + return readWhiteSpace(); + + return readOperator(); +} + +/** + * @brief Lexer::passEscapeCharacter + * @return returns true if escape sequence doesn't end with newline + */ +void Scanner::checkEscapeSequence(QChar quoteChar) +{ + if (m_src.peek() == QLatin1Char('\\')) { + m_src.move(); + QChar ch = m_src.peek(); + if (ch == QLatin1Char('\n') || ch.isNull()) + saveState(State_String, quoteChar); + } +} + +/** + reads single-line string literal, surrounded by ' or " quotes + */ +FormatToken Scanner::readStringLiteral(QChar quoteChar) +{ + QChar ch = m_src.peek(); + if (ch == quoteChar && m_src.peek(1) == quoteChar) { + saveState(State_MultiLineString, quoteChar); + return readMultiLineStringLiteral(quoteChar); + } + + while (ch != quoteChar && !ch.isNull()) { + checkEscapeSequence(quoteChar); + m_src.move(); + ch = m_src.peek(); + } + if (ch == quoteChar) + clearState(); + m_src.move(); + return FormatToken(Format_String, m_src.anchor(), m_src.length()); +} + +/** + reads multi-line string literal, surrounded by ''' or """ sequencies + */ +FormatToken Scanner::readMultiLineStringLiteral(QChar quoteChar) +{ + for (;;) { + QChar ch = m_src.peek(); + if (ch.isNull()) + break; + if (ch == quoteChar + && (m_src.peek(1) == quoteChar) + && (m_src.peek(2) == quoteChar)) { + clearState(); + m_src.move(); + m_src.move(); + m_src.move(); + break; + } + m_src.move(); + } + + return FormatToken(Format_String, m_src.anchor(), m_src.length()); +} + +/** + reads identifier and classifies it + */ +FormatToken Scanner::readIdentifier() +{ + QChar ch = m_src.peek(); + while (ch.isLetterOrNumber() || (ch == QLatin1Char('_'))) { + m_src.move(); + ch = m_src.peek(); + } + QString value = m_src.value(); + + Format tkFormat = Format_Identifier; + if (value == QLatin1String("self")) + tkFormat = Format_ClassField; + else if (m_builtins.contains(value)) + tkFormat = Format_Type; + else if (m_magics.contains(value)) + tkFormat = Format_MagicAttr; + else if (m_keywords.contains(value)) + tkFormat = Format_Keyword; + + return FormatToken(tkFormat, m_src.anchor(), m_src.length()); +} + +inline static bool isHexDigit(QChar ch) +{ + return (ch.isDigit() + || (ch >= QLatin1Char('a') && ch <= QLatin1Char('f')) + || (ch >= QLatin1Char('A') && ch <= QLatin1Char('F'))); +} + +inline static bool isOctalDigit(QChar ch) +{ + return (ch.isDigit() && ch != QLatin1Char('8') && ch != QLatin1Char('9')); +} + +inline static bool isBinaryDigit(QChar ch) +{ + return (ch == QLatin1Char('0') || ch == QLatin1Char('1')); +} + +inline static bool isValidIntegerSuffix(QChar ch) +{ + return (ch == QLatin1Char('l') || ch == QLatin1Char('L')); +} + +inline static bool isValidComplexSuffix(QChar ch) +{ + return (ch == QLatin1Char('j') || ch == QLatin1Char('J')); +} + +FormatToken Scanner::readNumber() +{ + if (!m_src.isEnd()) { + QChar ch = m_src.peek(); + if (ch.toLower() == QLatin1Char('b')) { + m_src.move(); + while (isBinaryDigit(m_src.peek())) + m_src.move(); + } else if (ch.toLower() == QLatin1Char('o')) { + m_src.move(); + while (isOctalDigit(m_src.peek())) + m_src.move(); + } else if (ch.toLower() == QLatin1Char('x')) { + m_src.move(); + while (isHexDigit(m_src.peek())) + m_src.move(); + } else { // either integer or float number + return readFloatNumber(); + } + if (isValidIntegerSuffix(m_src.peek())) + m_src.move(); + } + return FormatToken(Format_Number, m_src.anchor(), m_src.length()); +} + +FormatToken Scanner::readFloatNumber() +{ + enum + { + State_INTEGER, + State_FRACTION, + State_EXPONENT + } state; + state = (m_src.peek(-1) == QLatin1Char('.')) ? State_FRACTION : State_INTEGER; + + for (;;) { + QChar ch = m_src.peek(); + if (ch.isNull()) + break; + + if (state == State_INTEGER) { + if (ch == QLatin1Char('.')) + state = State_FRACTION; + else if (!ch.isDigit()) + break; + } else if (state == State_FRACTION) { + if (ch == QLatin1Char('e') || ch == QLatin1Char('E')) { + QChar next = m_src.peek(1); + QChar next2 = m_src.peek(2); + bool isExp = next.isDigit() + || (((next == QLatin1Char('-')) || (next == QLatin1Char('+'))) && next2.isDigit()); + if (isExp) { + m_src.move(); + state = State_EXPONENT; + } else { + break; + } + } else if (!ch.isDigit()) { + break; + } + } else if (!ch.isDigit()) { + break; + } + m_src.move(); + } + + QChar ch = m_src.peek(); + if ((state == State_INTEGER && (ch == QLatin1Char('l') || ch == QLatin1Char('L'))) + || (ch == QLatin1Char('j') || ch == QLatin1Char('J'))) + m_src.move(); + + return FormatToken(Format_Number, m_src.anchor(), m_src.length()); +} + +/** + reads single-line python comment, started with "#" + */ +FormatToken Scanner::readComment() +{ + QChar ch = m_src.peek(); + while (ch != QLatin1Char('\n') && !ch.isNull()) { + m_src.move(); + ch = m_src.peek(); + } + return FormatToken(Format_Comment, m_src.anchor(), m_src.length()); +} + +/** + reads single-line python doxygen comment, started with "##" + */ +FormatToken Scanner::readDoxygenComment() +{ + QChar ch = m_src.peek(); + while (ch != QLatin1Char('\n') && !ch.isNull()) { + m_src.move(); + ch = m_src.peek(); + } + return FormatToken(Format_Doxygen, m_src.anchor(), m_src.length()); +} + +/** + reads whitespace + */ +FormatToken Scanner::readWhiteSpace() +{ + while (m_src.peek().isSpace()) + m_src.move(); + return FormatToken(Format_Whitespace, m_src.anchor(), m_src.length()); +} + +/** + reads punctuation symbols, excluding some special + */ +FormatToken Scanner::readOperator() +{ + const QString EXCLUDED_CHARS = QLatin1String("\'\"_#"); + QChar ch = m_src.peek(); + while (ch.isPunct() && !EXCLUDED_CHARS.contains(ch)) { + m_src.move(); + ch = m_src.peek(); + } + return FormatToken(Format_Operator, m_src.anchor(), m_src.length()); +} + +void Scanner::clearState() +{ + m_state = 0; +} + +void Scanner::saveState(State state, QChar savedData) +{ + m_state = (state << 16) | static_cast<int>(savedData.unicode()); +} + +void Scanner::parseState(State &state, QChar &savedData) const +{ + state = static_cast<State>(m_state >> 16); + savedData = static_cast<ushort>(m_state); +} + +} // namespace Internal +} // namespace PythonEditor diff --git a/src/plugins/pythoneditor/tools/lexical/pythonscanner.h b/src/plugins/pythoneditor/tools/lexical/pythonscanner.h new file mode 100644 index 0000000000000000000000000000000000000000..f51ca19e81e8d8c673276f840a4b0e59ef7fb9c1 --- /dev/null +++ b/src/plugins/pythoneditor/tools/lexical/pythonscanner.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_SCANNER_H +#define PYTHONEDITOR_SCANNER_H + +#include "pythonformattoken.h" +#include "sourcecodestream.h" + +#include <QString> +#include <QSet> + +namespace PythonEditor { +namespace Internal { + +/** + * @brief The Scanner class - scans source code for highlighting only + */ +class Scanner +{ + Scanner(const Scanner &other); + void operator =(const Scanner &other); + +public: + enum State { + State_Default, + State_String, + State_MultiLineString + }; + + Scanner(const QChar *text, const int length); + ~Scanner(); + + void setState(int state); + int state() const; + FormatToken read(); + QString value(const FormatToken& tk) const; + +private: + FormatToken onDefaultState(); + + void checkEscapeSequence(QChar quoteChar); + FormatToken readStringLiteral(QChar quoteChar); + FormatToken readMultiLineStringLiteral(QChar quoteChar); + FormatToken readIdentifier(); + FormatToken readNumber(); + FormatToken readFloatNumber(); + FormatToken readComment(); + FormatToken readDoxygenComment(); + FormatToken readWhiteSpace(); + FormatToken readOperator(); + + void clearState(); + void saveState(State state, QChar savedData); + void parseState(State &state, QChar &savedData) const; + + SourceCodeStream m_src; + int m_state; + const QSet<QString> m_keywords; + const QSet<QString> m_magics; + const QSet<QString> m_builtins; +}; + +} // namespace Internal +} // namespace PythonEditor + +#endif // PYTHONEDITOR_SCANNER_H diff --git a/src/plugins/pythoneditor/tools/lexical/sourcecodestream.h b/src/plugins/pythoneditor/tools/lexical/sourcecodestream.h new file mode 100644 index 0000000000000000000000000000000000000000..25caed56aeef6f568cc032d0abc8a928de1d7305 --- /dev/null +++ b/src/plugins/pythoneditor/tools/lexical/sourcecodestream.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_SOURCECODESTREAM_H +#define PYTHONEDITOR_SOURCECODESTREAM_H + +#include <QString> + +namespace PythonEditor { +namespace Internal { + +class SourceCodeStream +{ +public: + SourceCodeStream(const QChar *text, int length) + :m_text(text) + ,m_textLength(length) + ,m_position(0) + ,m_markedPosition(0) + {} + + inline void setAnchor() + { + m_markedPosition = m_position; + } + + inline void move() + { + ++m_position; + } + + inline int length() const + { + return m_position - m_markedPosition; + } + + inline int anchor() const + { + return m_markedPosition; + } + + inline bool isEnd() const + { + return m_position >= m_textLength; + } + + inline QChar peek(int offset = 0) const + { + int pos = m_position + offset; + if (pos >= m_textLength) + return QLatin1Char('\0'); + return m_text[pos]; + } + + inline QString value() const + { + const QChar *start = m_text + m_markedPosition; + return QString(start, length()); + } + + inline QString value(int begin, int length) const + { + return QString(m_text + begin, length); + } + +private: + const QChar *m_text; + const int m_textLength; + int m_position; + int m_markedPosition; +}; + +} // namespace Internal +} // namespace PythonEditor + +#endif // PYTHONEDITOR_SOURCECODESTREAM_H diff --git a/src/plugins/pythoneditor/tools/pythonhighlighter.cpp b/src/plugins/pythoneditor/tools/pythonhighlighter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e59d4a013838bfc678754e17c2bb17df1fa98b5 --- /dev/null +++ b/src/plugins/pythoneditor/tools/pythonhighlighter.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +/** + * @brief The Highlighter class pre-highlights Python source using simple scanner. + * + * Highlighter doesn't highlight user types (classes and enumerations), syntax + * and semantic errors, unnecessary code, etc. It's implements only + * basic highlight mechanism. + * + * Main highlight procedure is highlightBlock(). + */ + +#include "pythonhighlighter.h" +#include "lexical/pythonscanner.h" + +#include <texteditor/basetextdocumentlayout.h> +#include <texteditor/basetextdocument.h> +#include <texteditor/fontsettings.h> +#include <texteditor/texteditorconstants.h> + +namespace PythonEditor { + +using namespace PythonEditor::Internal; + +/** + * @class PyEditor::Highlighter + * @brief Handles incremental lexical highlighting, but not semantic + * + * Incremental lexical highlighting works every time when any character typed + * or some text inserted (i.e. copied & pasted). + * Each line keeps associated scanner state - integer number. This state is the + * scanner context for next line. For example, 3 quotes begin a multiline + * string, and each line up to next 3 quotes has state 'MultiLineString'. + * + * @code + * def __init__: # Normal + * self.__doc__ = """ # MultiLineString (next line is inside) + * banana # MultiLineString + * """ # Normal + * @endcode + */ + +/// @return List that maps enum Format values to TextEditor plugin formats +QVector<TextEditor::TextStyle> initFormatCategories() +{ + QVector<TextEditor::TextStyle> categories(Format_FormatsAmount); + categories[Format_Number] = TextEditor::C_NUMBER; + categories[Format_String] = TextEditor::C_STRING; + categories[Format_Keyword] = TextEditor::C_KEYWORD; + categories[Format_Type] = TextEditor::C_TYPE; + categories[Format_ClassField] = TextEditor::C_FIELD; + categories[Format_MagicAttr] = TextEditor::C_JS_SCOPE_VAR; + categories[Format_Operator] = TextEditor::C_OPERATOR; + categories[Format_Comment] = TextEditor::C_COMMENT; + categories[Format_Doxygen] = TextEditor::C_DOXYGEN_COMMENT; + categories[Format_Whitespace] = TextEditor::C_VISUAL_WHITESPACE; + categories[Format_Identifier] = TextEditor::C_TEXT; + categories[Format_ImportedModule] = TextEditor::C_STRING; + + return categories; +} + +/// New instance created when opening any document in editor +PythonHighlighter::PythonHighlighter(TextEditor::BaseTextDocument *parent) : + TextEditor::SyntaxHighlighter(parent) +{ +} + +/// Instance destroyed when one of documents closed from editor +PythonHighlighter::~PythonHighlighter() +{ +} + +/** + QtCreator has own fonts&color settings. Highlighter wants get access to + this settings before highlightBlock() called first time. + Settings provided by PyEditor::EditorWidget class. + */ +void PythonHighlighter::setFontSettings(const TextEditor::FontSettings &fs) +{ + QVector<TextEditor::TextStyle> categories = initFormatCategories(); + m_formats = fs.toTextCharFormats(categories); + rehighlight(); +} + +/** + * @brief Highlighter::highlightBlock highlights single line of Python code + * @param text is single line without EOLN symbol. Access to all block data + * can be obtained through inherited currentBlock() method. + * + * This method receives state (int number) from previously highlighted block, + * scans block using received state and sets initial highlighting for current + * block. At the end, it saves internal state in current block. + */ +void PythonHighlighter::highlightBlock(const QString &text) +{ + int initialState = previousBlockState(); + if (initialState == -1) + initialState = 0; + setCurrentBlockState(highlightLine(text, initialState)); +} + +/** + * @return True if this keyword is acceptable at start of import line + */ +static inline +bool isImportKeyword(const QString &keyword) +{ + return (keyword == QLatin1String("import") + || keyword == QLatin1String("from")); +} + +/** + * @brief Highlight line of code, returns new block state + * @param text Source code to highlight + * @param initialState Initial state of scanner, retrieved from previous block + * @return Final state of scanner, should be saved with current block + */ +int PythonHighlighter::highlightLine(const QString &text, int initialState) +{ + Scanner scanner(text.constData(), text.size()); + scanner.setState(initialState); + + FormatToken tk; + bool hasOnlyWhitespace = true; + while ((tk = scanner.read()).format() != Format_EndOfBlock) { + Format format = tk.format(); + if (format == Format_Keyword) { + QString value = scanner.value(tk); + if (isImportKeyword(value) && hasOnlyWhitespace) { + setFormat(tk.begin(), tk.length(), m_formats[format]); + highlightImport(scanner); + break; + } + } + + setFormat(tk.begin(), tk.length(), m_formats[format]); + if (format != Format_Whitespace) + hasOnlyWhitespace = false; + } + return scanner.state(); +} + +/** + * @brief Highlights rest of line as import directive + */ +void PythonHighlighter::highlightImport(Scanner &scanner) +{ + FormatToken tk; + while ((tk = scanner.read()).format() != Format_EndOfBlock) { + Format format = tk.format(); + if (tk.format() == Format_Identifier) + format = Format_ImportedModule; + setFormat(tk.begin(), tk.length(), m_formats[format]); + } +} + +} // namespace PythonEditor diff --git a/src/plugins/pythoneditor/tools/pythonhighlighter.h b/src/plugins/pythoneditor/tools/pythonhighlighter.h new file mode 100644 index 0000000000000000000000000000000000000000..7a7b050325380eece89bd04f1f18e015d3bac258 --- /dev/null +++ b/src/plugins/pythoneditor/tools/pythonhighlighter.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_HIGHLIGHTER_H +#define PYTHONEDITOR_HIGHLIGHTER_H + +#include "../pythoneditor_global.h" +#include <texteditor/syntaxhighlighter.h> +#include <texteditor/fontsettings.h> +#include <QMap> + +namespace PythonEditor { + +namespace Internal { class Scanner; } + +class PYEDITOR_EXPORT PythonHighlighter : public TextEditor::SyntaxHighlighter +{ + Q_OBJECT +public: + explicit PythonHighlighter(TextEditor::BaseTextDocument *parent); + virtual ~PythonHighlighter(); + + void setFontSettings(const TextEditor::FontSettings &fs); + +protected: + virtual void highlightBlock(const QString &text); + +private: + int highlightLine(const QString &text, int initialState); + void highlightImport(Internal::Scanner &scanner); + QVector<QTextCharFormat> m_formats; +}; + +} // namespace PythonEditor + +#endif // PYTHONEDITOR_HIGHLIGHTER_H diff --git a/src/plugins/pythoneditor/tools/pythonindenter.cpp b/src/plugins/pythoneditor/tools/pythonindenter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..befa7f6e024fc8e7ba73f4da56379484e520ee53 --- /dev/null +++ b/src/plugins/pythoneditor/tools/pythonindenter.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "pythonindenter.h" +#include "lexical/pythonscanner.h" + +#include <texteditor/tabsettings.h> + +#include <QSet> + +namespace PythonEditor { + +// Tab size hardcoded as PEP8 style guide requires, but can be moved to settings +static const int TAB_SIZE = 4; + +PythonIndenter::PythonIndenter() +{ + m_jumpKeywords << QLatin1String("return") + << QLatin1String("yield") + << QLatin1String("break") + << QLatin1String("continue") + << QLatin1String("raise") + << QLatin1String("pass"); +} + +PythonIndenter::~PythonIndenter() +{ +} + +/** + * @brief Does given character change indentation level? + * @param ch Any value + * @return True if character increases indentation level at the next line + */ +bool PythonIndenter::isElectricCharacter(const QChar &ch) const +{ + return (ch == QLatin1Char(':')); +} + +/** + * @brief Indents one block (i.e. one line) of code + * @param doc Unused + * @param block Block that represents line + * @param typedChar Unused + * @param tabSettings An IDE tabulation settings + * + * Usually this method called once when you begin new line of code by pressing + * Enter. If Indenter reimplements indent() method, than indentBlock() may be + * called in other cases. + */ +void PythonIndenter::indentBlock(QTextDocument *document, + const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings &settings) +{ + Q_UNUSED(document); + Q_UNUSED(typedChar); + QTextBlock previousBlock = block.previous(); + if (previousBlock.isValid()) { + QString previousLine = previousBlock.text(); + int indentation = settings.indentationColumn(previousLine); + + if (isElectricLine(previousLine)) + indentation += TAB_SIZE; + else + indentation = qMax<int>(0, indentation + getIndentDiff(previousLine)); + + settings.indentLine(block, indentation); + } else { + // First line in whole document + settings.indentLine(block, 0); + } +} + +/// @return True if electric character is last non-space character at given string +bool PythonIndenter::isElectricLine(const QString &line) const +{ + if (line.isEmpty()) + return false; + + // trim spaces in 'if True: ' + int index = line.length() - 1; + while ((index > 0) && line[index].isSpace()) + --index; + + return isElectricCharacter(line[index]); +} + +/// @return negative indent diff if previous line breaks control flow branch +int PythonIndenter::getIndentDiff(const QString &previousLine) const +{ + Internal::Scanner sc(previousLine.constData(), previousLine.length()); + forever { + Internal::FormatToken tk = sc.read(); + if ((tk.format() == Internal::Format_Keyword) && m_jumpKeywords.contains(sc.value(tk))) + return -TAB_SIZE; + if (tk.format() != Internal::Format_Whitespace) + break; + } + return 0; +} + +} // namespace PythonEditor diff --git a/src/plugins/pythoneditor/tools/pythonindenter.h b/src/plugins/pythoneditor/tools/pythonindenter.h new file mode 100644 index 0000000000000000000000000000000000000000..be1e1f2d53163f59a61bc236f88f625b3aee3de2 --- /dev/null +++ b/src/plugins/pythoneditor/tools/pythonindenter.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYTHONEDITOR_INDENTER_H +#define PYTHONEDITOR_INDENTER_H + +#include "../pythoneditor_global.h" +#include <texteditor/indenter.h> +#include <QStringList> + +namespace PythonEditor { + +class PYEDITOR_EXPORT PythonIndenter : public TextEditor::Indenter +{ +public: + PythonIndenter(); + virtual ~PythonIndenter(); + + bool isElectricCharacter(const QChar &ch) const; + void indentBlock(QTextDocument *document, + const QTextBlock &block, + const QChar &typedChar, + const TextEditor::TabSettings &settings); + +protected: + bool isElectricLine(const QString &line) const; + int getIndentDiff(const QString &previousLine) const; + +private: + QStringList m_jumpKeywords; +}; + +} // namespace PythonEditor + +#endif // PYTHONEDITOR_INDENTER_H diff --git a/src/plugins/pythoneditor/wizard/pythonfilewizard.cpp b/src/plugins/pythoneditor/wizard/pythonfilewizard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c14e2668a0c52bb1509a72813e7dad6ae3fa1def --- /dev/null +++ b/src/plugins/pythoneditor/wizard/pythonfilewizard.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +/** + * @brief The FileWizard class - adds wizard for creating new Python source file + */ + +#include "pythonfilewizard.h" +#include "../pythoneditorconstants.h" + +#include <utils/filewizarddialog.h> +#include <texteditor/textfilewizard.h> + +#include <QWizard> + +namespace PythonEditor { + +/** + * @brief GetDefaultParams + * @return Default parameters for menu item "Files&Classes->Python->Python file" + */ +static const Core::BaseFileWizardParameters GetDefaultParams() +{ + Core::BaseFileWizardParameters p(Core::IWizard::FileWizard); + + p.setId(QLatin1String(Constants::C_PY_SOURCE_WIZARD_ID)); + p.setCategory(QLatin1String(Constants::C_PY_WIZARD_CATEGORY)); + p.setDisplayCategory(QLatin1String(Constants::C_PY_DISPLAY_CATEGORY)); + p.setDisplayName( + QObject::tr(Constants::EN_PY_SOURCE_DISPLAY_NAME)); + p.setDescription( + QObject::tr(Constants::EN_PY_SOURCE_DESCRIPTION)); + + return p; +} + +/** + * @brief Initialize wizard and add new option to "New..." dialog. + * @param parent + */ +FileWizard::FileWizard(QObject *parent) + :Core::BaseFileWizard(GetDefaultParams(), parent) +{ +} + +FileWizard::~FileWizard() +{ +} + +/** + * @brief FileWizard::createWizardDialog + * @param parent + * @param params + * @return + */ +QWizard *FileWizard::createWizardDialog(QWidget *parent, + const Core::WizardDialogParameters ¶ms) const +{ + Utils::FileWizardDialog *pDialog = new Utils::FileWizardDialog(parent); + pDialog->setWindowTitle(tr("New %1").arg(displayName())); + setupWizard(pDialog); + pDialog->setPath(params.defaultPath()); + foreach (QWizardPage *p, params.extensionPages()) + applyExtensionPageShortTitle(pDialog, pDialog->addPage(p)); + + return pDialog; +} + +Core::GeneratedFiles FileWizard::generateFiles(const QWizard *dialog, + QString *errorMessage) const +{ + Q_UNUSED(errorMessage) + + const Utils::FileWizardDialog *pWizard = + qobject_cast<const Utils::FileWizardDialog *>(dialog); + + QString folder = pWizard->path(); + QString name = pWizard->fileName(); + + name = Core::BaseFileWizard::buildFileName( + folder, name, QLatin1String(Constants::C_PY_EXTENSION)); + Core::GeneratedFile file(name); + file.setContents(QLatin1String(Constants::C_PY_SOURCE_CONTENT)); + file.setAttributes(Core::GeneratedFile::OpenEditorAttribute); + + return (Core::GeneratedFiles() << file); +} + +} // namespace PythonEditor diff --git a/src/plugins/pythoneditor/wizard/pythonfilewizard.h b/src/plugins/pythoneditor/wizard/pythonfilewizard.h new file mode 100644 index 0000000000000000000000000000000000000000..4bf976e7d351b4fb1c91d3a8a07c489b7ee24288 --- /dev/null +++ b/src/plugins/pythoneditor/wizard/pythonfilewizard.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef PYEDITOR_FILEWIZARD_H +#define PYEDITOR_FILEWIZARD_H + +#include <coreplugin/basefilewizard.h> + +namespace PythonEditor { + +class FileWizard : public Core::BaseFileWizard +{ + Q_OBJECT + +public: + explicit FileWizard(QObject *parent = 0); + virtual ~FileWizard(); + +protected: + virtual QWizard *createWizardDialog( + QWidget *parent, + const Core::WizardDialogParameters ¶ms) const; + + virtual Core::GeneratedFiles generateFiles( + const QWizard *dialog, + QString *errorMessage) const; +}; + +} // namespace PythonEditor + +#endif // PYEDITOR_FILEWIZARD_H