From 0cba7a96b2b1a43c3ad029b375cffcee118a1aa4 Mon Sep 17 00:00:00 2001 From: stv Date: Wed, 24 May 2017 13:50:33 +0300 Subject: [PATCH] PyEditor: introduce auto-completion feature --- src/LightApp/resources/LightApp.xml | 1 + src/PyViewer/PyViewer_Settings.cxx | 3 + tools/PyEditor/src/CMakeLists.txt | 4 + tools/PyEditor/src/PyEditor_Completer.cxx | 264 ++++++++++++++++ tools/PyEditor/src/PyEditor_Completer.h | 73 +++++ tools/PyEditor/src/PyEditor_Editor.cxx | 91 +++++- tools/PyEditor/src/PyEditor_Editor.h | 29 +- tools/PyEditor/src/PyEditor_Keywords.cxx | 243 +++++++++++++++ tools/PyEditor/src/PyEditor_Keywords.h | 81 +++++ tools/PyEditor/src/PyEditor_PyHighlighter.cxx | 295 +++++++----------- tools/PyEditor/src/PyEditor_PyHighlighter.h | 18 +- tools/PyEditor/src/PyEditor_Settings.cxx | 28 +- tools/PyEditor/src/PyEditor_Settings.h | 9 +- tools/PyEditor/src/PyEditor_SettingsDlg.cxx | 12 + tools/PyEditor/src/PyEditor_SettingsDlg.h | 4 +- tools/PyEditor/src/PyEditor_StdSettings.cxx | 24 +- tools/PyEditor/src/python/PyEditorPy.sip | 7 + tools/PyEditor/src/resources/about.txt | 1 + .../resources/translations/PyEditor_msg_en.ts | 24 ++ .../resources/translations/PyEditor_msg_fr.ts | 24 ++ .../resources/translations/PyEditor_msg_ja.ts | 24 ++ 21 files changed, 1053 insertions(+), 206 deletions(-) create mode 100644 tools/PyEditor/src/PyEditor_Completer.cxx create mode 100644 tools/PyEditor/src/PyEditor_Completer.h create mode 100644 tools/PyEditor/src/PyEditor_Keywords.cxx create mode 100644 tools/PyEditor/src/PyEditor_Keywords.h diff --git a/src/LightApp/resources/LightApp.xml b/src/LightApp/resources/LightApp.xml index ace4c880f..3f74985a9 100644 --- a/src/LightApp/resources/LightApp.xml +++ b/src/LightApp/resources/LightApp.xml @@ -237,6 +237,7 @@ +
diff --git a/src/PyViewer/PyViewer_Settings.cxx b/src/PyViewer/PyViewer_Settings.cxx index d86c8373e..90259e380 100644 --- a/src/PyViewer/PyViewer_Settings.cxx +++ b/src/PyViewer/PyViewer_Settings.cxx @@ -50,6 +50,8 @@ void PyViewer_Settings::load() setTabSpaceVisible( myResMgr->booleanValue( group, option( snTabSpaceVisible ), tabSpaceVisible() ) ); setTabSize( myResMgr->integerValue( group, option( snTabSize ), tabSize() ) ); setFont( myResMgr->fontValue( group, option( snFont ), font() ) ); + setCompletionPolicy( myResMgr->integerValue( group, option( snCompletionPolicy ), + completionPolicy() ) ); } void PyViewer_Settings::save() @@ -65,4 +67,5 @@ void PyViewer_Settings::save() myResMgr->setValue( group, option( snTabSpaceVisible ), tabSpaceVisible() ); myResMgr->setValue( group, option( snTabSize ), tabSize() ); myResMgr->setValue( group, option( snFont ), font() ); + myResMgr->setValue( group, option( snCompletionPolicy ), completionPolicy() ); } diff --git a/tools/PyEditor/src/CMakeLists.txt b/tools/PyEditor/src/CMakeLists.txt index 94f88dae6..2715e6ebd 100644 --- a/tools/PyEditor/src/CMakeLists.txt +++ b/tools/PyEditor/src/CMakeLists.txt @@ -44,6 +44,8 @@ SET(_link_LIBRARIES ${PLATFORM_LIBS} ${QT_LIBRARIES}) SET(_moc_HEADERS PyEditor_Editor.h PyEditor_LineNumberArea.h + PyEditor_Keywords.h + PyEditor_Completer.h PyEditor_PyHighlighter.h PyEditor_SettingsDlg.h PyEditor_Window.h @@ -83,6 +85,8 @@ QT_ADD_RESOURCES(_rcc_SOURCES ${_rcc_RESOURCES}) SET(_other_SOURCES PyEditor_Editor.cxx PyEditor_LineNumberArea.cxx + PyEditor_Keywords.cxx + PyEditor_Completer.cxx PyEditor_PyHighlighter.cxx PyEditor_Settings.cxx PyEditor_SettingsDlg.cxx diff --git a/tools/PyEditor/src/PyEditor_Completer.cxx b/tools/PyEditor/src/PyEditor_Completer.cxx new file mode 100644 index 000000000..bee9d992e --- /dev/null +++ b/tools/PyEditor/src/PyEditor_Completer.cxx @@ -0,0 +1,264 @@ +// Copyright (C) 2015-2016 OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyEditor_Completer.cxx +// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com) +// + +#include "PyEditor_Completer.h" + +#include "PyEditor_Editor.h" +#include "PyEditor_Keywords.h" + +#include +#include +#include +#include +#include +#include +#include + +/*! + \brief Constructor. +*/ +PyEditor_Completer::PyEditor_Completer( PyEditor_Editor* editor, + PyEditor_Keywords* std, PyEditor_Keywords* user ) + : QCompleter( editor ), + myEditor( editor ), + myTimer( 0 ), + myStdKeywords( std ), + myUserKeywords( user ) +{ + setWidget( editor ); + setCompletionMode(QCompleter::PopupCompletion); + + connect(this, SIGNAL( activated( const QString& ) ), + this, SLOT( onActivated( const QString& ) ) ); + connect(editor, SIGNAL( textChanged() ), this, SLOT( onTextChanged() ) ); + connect(myStdKeywords, SIGNAL( keywordsChanged() ), + this, SLOT( onKeywordsChanged() ) ); + connect(myUserKeywords, SIGNAL( keywordsChanged() ), + this, SLOT( onKeywordsChanged() ) ); + + updateKeywords(); +} + +/*! + \brief Destructor. +*/ +PyEditor_Completer::~PyEditor_Completer() +{ +} + +/*! + \brief Perform the completion if it possible. +*/ +void PyEditor_Completer::perform() +{ + QString prefix = completionText(); + setCompletionPrefix( prefix ); + + if ( !completionPrefix().isEmpty() && + ( completionCount() > 1 || ( completionCount() == 1 && + currentCompletion() != completionPrefix() ) ) ) + complete(completionRect()); + else + uncomplete(); +} + +/*! + \brief Hide the completer's popup menu. +*/ +void PyEditor_Completer::uncomplete() +{ + if ( popup() ) + popup()->hide(); +} + +/*! + \brief Handling 'Enter' key. +*/ +bool PyEditor_Completer::eventFilter(QObject* o, QEvent* e) +{ + bool res = false; + if ( e->type() == QEvent::KeyPress && popup()->isVisible() ) { + QKeyEvent* ke = (QKeyEvent*)e; + if ( ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) { + res = true; + setCurrentRow(popup()->currentIndex().row()); + onActivated(currentCompletion()); + } + } + + if ( !res ) + res = QCompleter::eventFilter(o, e); + + return res; +} + +/*! + \brief Perform delayed completion. +*/ +void PyEditor_Completer::onTimeout() +{ + perform(); +} + +/*! + \brief Invoked when text changed in editor. +*/ +void PyEditor_Completer::onTextChanged() +{ + uncomplete(); + if ( myEditor->completionPolicy() == PyEditor_Editor::Auto || + myEditor->completionPolicy() == PyEditor_Editor::Always ) + triggerComplete(); +} + +/*! + \brief Invoked when keywords changed in editor. +*/ +void PyEditor_Completer::onKeywordsChanged() +{ + updateKeywords(); +} + +/*! + \brief Insert selected completion into editor. +*/ +void PyEditor_Completer::onActivated( const QString& text) +{ + QPoint rng = completionRange(); + QTextCursor cursor = myEditor->textCursor(); + cursor.setPosition(cursor.position() - rng.y() + rng.x() - 1, + QTextCursor::KeepAnchor); + cursor.insertText(text); + uncomplete(); +} + +/*! + \brief Get the rectangle for completion popup. + \return completion popup rect +*/ +QRect PyEditor_Completer::completionRect() const +{ + QRect res = myEditor->cursorRect(myEditor->textCursor()); + res.setWidth(popup()->sizeHint().width()); + res.translate(myEditor->document()->documentMargin(), + myEditor->document()->documentMargin()); + return res; +} + +/*! + \brief Get the current completion prefix from editor. + \return completion prefix string +*/ +QString PyEditor_Completer::completionText() const +{ + QString prefix; + if ( myEditor ) { + QString txt = myEditor->textCursor().block().text(); + if ( !txt.isEmpty() ) { + QPoint range = completionRange(); + prefix = txt.mid( range.x(), range.y() - range.x() + 1 ); + } + } + return prefix; +} + +/*! + \brief Get position of completion prefix in editor. + \return begin and end of completion prefix +*/ +QPoint PyEditor_Completer::completionRange() const +{ + QPoint range; + + if ( myEditor ) { + QTextCursor cursor = myEditor->textCursor(); + QString txt = cursor.block().text(); + int beg = 0; + int end = cursor.positionInBlock() - 1; + + QRegExp rx("[A-Za-z]{1}\\w*$"); + int pos = rx.indexIn(txt.mid(beg, end - beg + 1)); + + if ( pos >= 0 ) + beg = pos; + + range = QPoint(beg, end); + } + + return range; +} + +/*! + \brief Schedule the delayed completion. +*/ +void PyEditor_Completer::triggerComplete() +{ + if ( !myTimer ) { + myTimer = new QTimer( this ); + myTimer->setSingleShot( true ); + myTimer->setInterval( 200 ); + + connect( myTimer, SIGNAL( timeout() ), this, SLOT( onTimeout() ) ); + } + + if ( myTimer->isActive() ) + myTimer->stop(); + myTimer->start(); +} + +/*! + \brief Updates the keywords list in completer. +*/ +void PyEditor_Completer::updateKeywords() +{ + QStandardItemModel* model = new QStandardItemModel( this ); + KeywordMap kwMap = keywords(); + for ( KeywordMap::const_iterator it = kwMap.begin(); it != kwMap.end(); ++it ) { + QStandardItem* item = new QStandardItem( it.key() ); + if ( it.value().isValid() ) + item->setForeground( it.value() ); + model->appendRow( item ); + } + setModel( model ); +} + +/*! + \brief Gets the keywords list. + \return keyword string list +*/ +PyEditor_Completer::KeywordMap PyEditor_Completer::keywords() const +{ + KeywordMap res; + QList kwDicts; + kwDicts << myStdKeywords << myUserKeywords; + + for ( QList::iterator itr = kwDicts.begin(); itr != kwDicts.end(); ++itr ) { + PyEditor_Keywords* dict = *itr; + QStringList kwList = dict->keywords(); + for ( QStringList::const_iterator it = kwList.begin(); it != kwList.end(); ++it ) { + if ( !res.contains( *it ) ) { + res.insert( *it, dict->color( *it ) ); + } + } + } + return res; +} diff --git a/tools/PyEditor/src/PyEditor_Completer.h b/tools/PyEditor/src/PyEditor_Completer.h new file mode 100644 index 000000000..01e0f4efc --- /dev/null +++ b/tools/PyEditor/src/PyEditor_Completer.h @@ -0,0 +1,73 @@ +// Copyright (C) 2015-2016 OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyEditor_Completer.h +// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com) +// + +#ifndef PYEDITOR_COMPLETER_H +#define PYEDITOR_COMPLETER_H + +#include + +class QTimer; +class PyEditor_Editor; +class PyEditor_Keywords; + +class PyEditor_Completer : public QCompleter +{ + Q_OBJECT + +public: + PyEditor_Completer( PyEditor_Editor*, + PyEditor_Keywords*, PyEditor_Keywords* ); + virtual ~PyEditor_Completer(); + + void perform(); + + void uncomplete(); + + virtual bool eventFilter(QObject*, QEvent*); + +private Q_SLOTS: + void onTimeout(); + void onTextChanged(); + void onKeywordsChanged(); + void onActivated( const QString& ); + +protected: + QRect completionRect() const; + QString completionText() const; + QPoint completionRange() const; + +private: + typedef QMap KeywordMap; + +private: + KeywordMap keywords() const; + void updateKeywords(); + void triggerComplete(); + +private: + PyEditor_Editor* myEditor; + QTimer* myTimer; + PyEditor_Keywords* myStdKeywords; + PyEditor_Keywords* myUserKeywords; +}; + +#endif diff --git a/tools/PyEditor/src/PyEditor_Editor.cxx b/tools/PyEditor/src/PyEditor_Editor.cxx index fff2e0425..6fe34f196 100644 --- a/tools/PyEditor/src/PyEditor_Editor.cxx +++ b/tools/PyEditor/src/PyEditor_Editor.cxx @@ -23,11 +23,15 @@ #include "PyEditor_Editor.h" #include "PyEditor_LineNumberArea.h" #include "PyEditor_PyHighlighter.h" +#include "PyEditor_Completer.h" #include "PyEditor_Settings.h" +#include "PyEditor_Keywords.h" #include #include +#include + /*! \class PyEditor_Editor \brief Widget to show / edit Python scripts. @@ -38,20 +42,28 @@ \param parent parent widget */ PyEditor_Editor::PyEditor_Editor( QWidget* parent ) - : QPlainTextEdit( parent ) + : QPlainTextEdit( parent ), + myCompletionPolicy( Always ) { + myStdKeywords = new PyEditor_StandardKeywords( this ); + myUserKeywords = new PyEditor_Keywords( this ); + myUserKeywords->append( "print", 0, Qt::red ); + // Set up line number area myLineNumberArea = new PyEditor_LineNumberArea( this ); myLineNumberArea->setMouseTracking( true ); // Set up syntax highighter - mySyntaxHighlighter = new PyEditor_PyHighlighter( this->document() ); + mySyntaxHighlighter = new PyEditor_PyHighlighter( this->document(), + myStdKeywords, myUserKeywords ); // Set-up settings PyEditor_Settings* settings = PyEditor_Settings::settings(); if ( settings ) setSettings( *settings ); + myCompleter = new PyEditor_Completer( this, myStdKeywords, myUserKeywords ); + // Connect signals connect( this, SIGNAL( blockCountChanged( int ) ), this, SLOT( updateLineNumberAreaWidth( int ) ) ); connect( this, SIGNAL( updateRequest( QRect, int ) ), this, SLOT( updateLineNumberArea( QRect, int ) ) ); @@ -98,6 +110,9 @@ void PyEditor_Editor::setSettings( const PyEditor_Settings& settings ) // Set size white spaces setTabStopWidth( mySettings.tabSize() * 10 ); + // Set completion policy + setCompletionPolicy( (CompletionPolicy)mySettings.completionPolicy() ); + // Update current line highlight updateHighlightCurrentLine(); @@ -108,6 +123,54 @@ void PyEditor_Editor::setSettings( const PyEditor_Settings& settings ) viewport()->update(); } +/*! + \brief Gets the current completion policy + \return completion policy +*/ +PyEditor_Editor::CompletionPolicy PyEditor_Editor::completionPolicy() const +{ + return myCompletionPolicy; +} + +/*! + \brief Sets the current completion policy + \param policy completion policy +*/ +void PyEditor_Editor::setCompletionPolicy( const CompletionPolicy& policy ) +{ + myCompletionPolicy = policy; +} + +/*! + \brief Gets the all user keywords. + \param event key press event + \return keyword string list +*/ +QStringList PyEditor_Editor::keywords() const +{ + return myUserKeywords->keywords(); +} + +/*! + \brief Add the user keywords. + \param kws keywords string list + \param type keywords type + \param color keywords color +*/ +void PyEditor_Editor::appendKeywords( const QStringList& kws, int type, const QColor& color ) +{ + myUserKeywords->append( kws, type, color ); +} + +/*! + \brief Remove the user keywords. + \param kws keywords string list +*/ +void PyEditor_Editor::removeKeywords( const QStringList& kws ) +{ + myUserKeywords->remove( kws ); +} + /*! Delete current selection contents. */ @@ -151,6 +214,12 @@ void PyEditor_Editor::keyPressEvent( QKeyEvent* event ) aCursor.endEditBlock(); event->accept(); } + else if ( aKey == Qt::Key_Space && aCtrl && !aShift && + ( completionPolicy() == Manual || completionPolicy() == Always ) ) + { + myCompleter->perform(); + event->accept(); + } else if ( event == QKeySequence::MoveToStartOfLine || event == QKeySequence::SelectStartOfLine ) { QTextCursor aCursor = this->textCursor(); @@ -824,3 +893,21 @@ QString PyEditor_Editor::text() const { return toPlainText(); } + +/*! + \brief Get user keywords dictionary. + \return keywords dictionary +*/ +PyEditor_Keywords* PyEditor_Editor::userKeywords() const +{ + return myUserKeywords; +} + +/*! + \brief Get standard keywords dictionary. + \return keywords dictionary +*/ +PyEditor_Keywords* PyEditor_Editor::standardKeywords() const +{ + return myStdKeywords; +} diff --git a/tools/PyEditor/src/PyEditor_Editor.h b/tools/PyEditor/src/PyEditor_Editor.h index 026635673..d37c1a860 100644 --- a/tools/PyEditor/src/PyEditor_Editor.h +++ b/tools/PyEditor/src/PyEditor_Editor.h @@ -28,20 +28,32 @@ #include +class PyEditor_Keywords; +class PyEditor_Completer; class PyEditor_PyHighlighter; class PYEDITOR_EXPORT PyEditor_Editor : public QPlainTextEdit { Q_OBJECT +public: + typedef enum { None, Auto, Manual, Always } CompletionPolicy; + public: PyEditor_Editor( QWidget* = 0 ); virtual ~PyEditor_Editor(); - void setSettings( const PyEditor_Settings& ); + void setSettings( const PyEditor_Settings& ); const PyEditor_Settings& settings() const; QString text() const; + QStringList keywords() const; + void appendKeywords( const QStringList&, int, const QColor& = QColor() ); + void removeKeywords( const QStringList& ); + + CompletionPolicy completionPolicy() const; + void setCompletionPolicy( const CompletionPolicy& ); + public Q_SLOTS: void deleteSelected(); void append( const QString& ); @@ -51,7 +63,10 @@ protected: virtual void keyPressEvent( QKeyEvent* ); virtual void resizeEvent( QResizeEvent* ); virtual void paintEvent( QPaintEvent* ); - + + PyEditor_Keywords* userKeywords() const; + PyEditor_Keywords* standardKeywords() const; + private Q_SLOTS: void updateHighlightCurrentLine(); void matchParentheses(); @@ -72,13 +87,19 @@ private: int lineIndent(); void tabIndentation( bool ); void indentSelection( bool ); - + int findFirstNonSpace( const QString& ); - + QWidget* myLineNumberArea; PyEditor_PyHighlighter* mySyntaxHighlighter; + PyEditor_Completer* myCompleter; PyEditor_Settings mySettings; + PyEditor_Keywords* myStdKeywords; + PyEditor_Keywords* myUserKeywords; + + CompletionPolicy myCompletionPolicy; + friend class PyEditor_LineNumberArea; }; diff --git a/tools/PyEditor/src/PyEditor_Keywords.cxx b/tools/PyEditor/src/PyEditor_Keywords.cxx new file mode 100644 index 000000000..ecc9fe673 --- /dev/null +++ b/tools/PyEditor/src/PyEditor_Keywords.cxx @@ -0,0 +1,243 @@ +// Copyright (C) 2015-2016 OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyEditor_Keywords.cxx +// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com) +// + +#include "PyEditor_Keywords.h" + +#include + +/*! + \brief PyEditor_Keywords +*/ + +/*! + \brief Constructor. +*/ +PyEditor_Keywords::PyEditor_Keywords( QObject* parent ) + : QObject( parent ) +{ +} + +/*! + \brief Destructor. +*/ +PyEditor_Keywords::~PyEditor_Keywords() +{ +} + + +QList PyEditor_Keywords::types() const +{ + QMap map; + for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) { + map.insert( it.value().type, false ); + } + return map.keys(); +} + +/*! + \brief Gets the colors list. + \return color list +*/ +QList PyEditor_Keywords::colors() const +{ + QList list; + QSet set; + for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) { + const QColor& color = it.value().color; + if ( !set.contains( color.rgba() ) ) { + list.append( color ); + set.insert( color.rgba() ); + } + } + return list; +} + +/*! + \brief Gets the keyword type. + \return type number +*/ +int PyEditor_Keywords::type( const QString& keyword ) const +{ + return myKeywords.contains(keyword) ? myKeywords[keyword].type : -1; +} + +/*! + \brief Gets the keyword color. + \return color +*/ +QColor PyEditor_Keywords::color( const QString& keyword ) const +{ + return myKeywords.contains(keyword) ? myKeywords[keyword].color : QColor(); +} + +/*! + \brief Gets all keywords. + \return keywords string list +*/ +QStringList PyEditor_Keywords::keywords() const +{ + return myKeywords.keys(); +} + +/*! + \brief Gets all keywords of specified type. + \return keywords string list +*/ +QStringList PyEditor_Keywords::keywords( int type ) const +{ + QStringList keywords; + for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) { + if ( it.value().type == type ) + keywords.append( it.key() ); + } + return keywords; +} + +/*! + \brief Gets all keywords with specified color. + \return keywords string list +*/ +QStringList PyEditor_Keywords::keywords( const QColor& color ) const +{ + QStringList keywords; + for ( KeywordMap::const_iterator it = myKeywords.begin(); it != myKeywords.end(); ++it ) { + if ( it.value().color == color ) + keywords.append( it.key() ); + } + return keywords; +} + +/*! + \brief Append keyword with type and color. +*/ +void PyEditor_Keywords::append( const QString& keyword, + int type, const QColor& color ) +{ + append( QStringList() << keyword, type, color ); +} + +/*! + \brief Append keyword list with type and color. +*/ +void PyEditor_Keywords::append( const QStringList& keywords, + int type, const QColor& color ) +{ + bool modif = false; + for ( QStringList::const_iterator it = keywords.begin(); it != keywords.end(); ++it ) { + const QString& kw = *it; + bool changed = false; + if ( !myKeywords.contains( kw ) ) { + myKeywords.insert( kw, KeywordInfo() ); + changed = true; + } + KeywordInfo& info = myKeywords[kw]; + changed = changed || info.type != type || info.color != color; + info.type = type; + info.color = color; + + modif = modif || changed; + } + + if ( modif ) + Q_EMIT keywordsChanged(); +} + +/*! + \brief Remove all keywords with specified type. +*/ +void PyEditor_Keywords::remove( int type ) +{ + remove( keywords( type ) ); +} + +/*! + \brief Remove keyword. +*/ +void PyEditor_Keywords::remove( const QString& keyword ) +{ + remove( QStringList() << keyword ); +} + +/*! + \brief Remove keywords. +*/ +void PyEditor_Keywords::remove( const QStringList& keywords ) +{ + bool changed = false; + for ( QStringList::const_iterator it = keywords.begin(); it != keywords.end(); ++it ) { + if ( myKeywords.contains( *it ) ) { + myKeywords.remove( *it ); + changed = true; + } + } + if ( changed ) + Q_EMIT keywordsChanged(); +} + +/*! + \brief Remove all keywords. +*/ +void PyEditor_Keywords::clear() +{ + if ( !myKeywords.isEmpty() ) { + myKeywords.clear(); + Q_EMIT keywordsChanged(); + } +} + +/*! + \brief PyEditor_StandardKeywords +*/ + +PyEditor_StandardKeywords::PyEditor_StandardKeywords( QObject* parent ) + : PyEditor_Keywords( parent ) +{ + QStringList aBase; + aBase << "and" << "as" << "assert" << "break" << "class" << "continue" + << "def" << "elif" << "else" << "except" << "exec" << "finally" + << "False" << "for" << "from" << "global" << "if" << "import" + << "in" << "is" << "lambda" << "None" << "not" << "or" << "pass" + << "print" << "raise" << "return" << "True" << "try" << "while" + << "with" << "yield"; + append( aBase, Base, Qt::blue ); + + QStringList anExcept; + anExcept << "ArithmeticError" << "AssertionError" << "AttributeError" + << "EnvironmentError" << "EOFError" << "Exception" + << "FloatingPointError" << "ImportError" << "IndentationError" + << "IndexError" << "IOError" << "KeyboardInterrupt" << "KeyError" + << "LookupError" << "MemoryError" << "NameError" << "OSError" + << "NotImplementedError" << "OverflowError" << "ReferenceError" + << "RuntimeError" << "StandardError" << "StopIteration" + << "SyntaxError" << "SystemError" << "SystemExit" << "TabError" + << "TypeError" << "UnboundLocalError" << "UnicodeDecodeError" + << "UnicodeEncodeError" << "UnicodeError" << "UnicodeTranslateError" + << "ValueError" << "WindowsError" << "ZeroDivisionError" + << "Warning" << "UserWarning" << "DeprecationWarning" + << "PendingDeprecationWarning" << "SyntaxWarning" + << "OverflowWarning" << "RuntimeWarning" << "FutureWarning"; + append( anExcept, Exceptions, Qt::magenta ); +} + +PyEditor_StandardKeywords::~PyEditor_StandardKeywords() +{ +} diff --git a/tools/PyEditor/src/PyEditor_Keywords.h b/tools/PyEditor/src/PyEditor_Keywords.h new file mode 100644 index 000000000..03f550681 --- /dev/null +++ b/tools/PyEditor/src/PyEditor_Keywords.h @@ -0,0 +1,81 @@ +// Copyright (C) 2015-2016 OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File : PyEditor_Keywords.h +// Author : Sergey TELKOV, Open CASCADE S.A.S. (sergey.telkov@opencascade.com) +// + +#ifndef PYEDITOR_KEYWORDS_H +#define PYEDITOR_KEYWORDS_H + +#include +#include +#include +#include + +class PyEditor_Keywords : public QObject +{ + Q_OBJECT + +public: + PyEditor_Keywords( QObject* = 0 ); + virtual ~PyEditor_Keywords(); + + QList types() const; + QList colors() const; + + int type( const QString& ) const; + QColor color( const QString& ) const; + + QStringList keywords() const; + QStringList keywords( int ) const; + QStringList keywords( const QColor& ) const; + + void append( const QString&, int, const QColor& = QColor() ); + void append( const QStringList&, int, const QColor& = QColor() ); + + void remove( int ); + void remove( const QString& ); + void remove( const QStringList& ); + + void clear(); + +Q_SIGNALS: + void keywordsChanged(); + +private: + typedef struct { int type; QColor color; } KeywordInfo; + typedef QMap KeywordMap; + +private: + KeywordMap myKeywords; +}; + +class PyEditor_StandardKeywords : public PyEditor_Keywords +{ + Q_OBJECT + +public: + typedef enum { Base, Exceptions } KeywordType; + +public: + PyEditor_StandardKeywords( QObject* = 0 ); + virtual ~PyEditor_StandardKeywords(); +}; + +#endif diff --git a/tools/PyEditor/src/PyEditor_PyHighlighter.cxx b/tools/PyEditor/src/PyEditor_PyHighlighter.cxx index 70ff0186a..70a92748b 100644 --- a/tools/PyEditor/src/PyEditor_PyHighlighter.cxx +++ b/tools/PyEditor/src/PyEditor_PyHighlighter.cxx @@ -22,6 +22,10 @@ #include "PyEditor_PyHighlighter.h" +#include "PyEditor_Keywords.h" + +#include + #define NORMAL 0 #define TRIPLESINGLE 1 #define TRIPLEDOUBLE 2 @@ -53,192 +57,20 @@ void PyEditor_PyHighlighter::TextBlockData::insert( PyEditor_PyHighlighter::Pare \brief Constructor. \param theDocument container for structured rich text documents. */ -PyEditor_PyHighlighter::PyEditor_PyHighlighter( QTextDocument* theDocument ) - : QSyntaxHighlighter( theDocument ) -{ - initialize(); -} - -/*! - \brief Initialization rules. -*/ -void PyEditor_PyHighlighter::initialize() +PyEditor_PyHighlighter::PyEditor_PyHighlighter( QTextDocument* theDocument, + PyEditor_Keywords* std, + PyEditor_Keywords* user ) + : QSyntaxHighlighter( theDocument ), + myStdKeywords( std ), + myUserKeywords( user ) { - HighlightingRule aRule; - - // Keywords - keywordFormat.setForeground( Qt::blue ); - QStringList aKeywords = keywords(); - foreach ( const QString& keyword, aKeywords ) - { - aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) ); - aRule.format = keywordFormat; - aRule.capture = 0; - highlightingRules.append( aRule ); - } - - // Special keywords - specialFromat.setForeground( Qt::magenta ); - QStringList aSpecialKeywords = specialKeywords(); - foreach ( const QString& keyword, aSpecialKeywords ) - { - aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) ); - aRule.format = specialFromat; - aRule.capture = 0; - highlightingRules.append( aRule ); - } - - // Reference to the current instance of the class - referenceClassFormat.setForeground( QColor( 179, 143, 0 ) ); - referenceClassFormat.setFontItalic( true ); - aRule.pattern = QRegExp( "\\bself\\b" ); - aRule.format = referenceClassFormat; - aRule.capture = 0; - highlightingRules.append( aRule ); - - // Numbers - numberFormat.setForeground( Qt::darkMagenta ); - aRule.pattern = QRegExp( "\\b([-+])?(\\d+(\\.)?\\d*|\\d*(\\.)?\\d+)(([eE]([-+])?)?\\d+)?\\b" ); - aRule.format = numberFormat; - aRule.capture = 0; - highlightingRules.append( aRule ); - - // String qoutation - quotationFormat.setForeground( Qt::darkGreen ); - aRule.pattern = QRegExp( "(?:'[^']*'|\"[^\"]*\")" ); - aRule.pattern.setMinimal( true ); - aRule.format = quotationFormat; - aRule.capture = 0; - highlightingRules.append( aRule ); - // Function names - functionFormat.setFontWeight( QFont::Bold ); - aRule.pattern = QRegExp( "(?:def\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" ); - aRule.capture = 1; - aRule.format = functionFormat; - highlightingRules.append( aRule ); - - // Class names - classFormat.setForeground( Qt::darkBlue ); - classFormat.setFontWeight( QFont::Bold ); - aRule.pattern = QRegExp( "(?:class\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" ); - aRule.capture = 1; - aRule.format = classFormat; - highlightingRules.append( aRule ); - - // Multi line comments - multiLineCommentFormat.setForeground( Qt::darkRed ); - tripleQuotesExpression = QRegExp( "(:?\"[\"]\".*\"[\"]\"|'''.*''')" ); - aRule.pattern = tripleQuotesExpression; - aRule.pattern.setMinimal( true ); - aRule.format = multiLineCommentFormat; - aRule.capture = 0; - highlightingRules.append( aRule ); - - tripleSingleExpression = QRegExp( "'''(?!\")" ); - tripleDoubleExpression = QRegExp( "\"\"\"(?!')" ); - - // Single comments - singleLineCommentFormat.setForeground( Qt::darkGray ); - aRule.pattern = QRegExp( "#[^\n]*" ); - aRule.format = singleLineCommentFormat; - aRule.capture = 0; - highlightingRules.append( aRule ); -} - -/*! - \return string list of Python keywords. - */ -QStringList PyEditor_PyHighlighter::keywords() -{ - QStringList aKeywords; - aKeywords << "and" - << "as" - << "assert" - << "break" - << "class" - << "continue" - << "def" - << "elif" - << "else" - << "except" - << "exec" - << "finally" - << "False" - << "for" - << "from" - << "global" - << "if" - << "import" - << "in" - << "is" - << "lambda" - << "None" - << "not" - << "or" - << "pass" - << "print" - << "raise" - << "return" - << "True" - << "try" - << "while" - << "with" - << "yield"; - return aKeywords; -} + connect(myStdKeywords, SIGNAL( keywordsChanged() ), + this, SLOT( onKeywordsChanged() ) ); + connect(myUserKeywords, SIGNAL( keywordsChanged() ), + this, SLOT( onKeywordsChanged() ) ); -/*! - \return string list of special Python keywords. -*/ -QStringList PyEditor_PyHighlighter::specialKeywords() -{ - QStringList aSpecialKeywords; - aSpecialKeywords << "ArithmeticError" - << "AssertionError" - << "AttributeError" - << "EnvironmentError" - << "EOFError" - << "Exception" - << "FloatingPointError" - << "ImportError" - << "IndentationError" - << "IndexError" - << "IOError" - << "KeyboardInterrupt" - << "KeyError" - << "LookupError" - << "MemoryError" - << "NameError" - << "NotImplementedError" - << "OSError" - << "OverflowError" - << "ReferenceError" - << "RuntimeError" - << "StandardError" - << "StopIteration" - << "SyntaxError" - << "SystemError" - << "SystemExit" - << "TabError" - << "TypeError" - << "UnboundLocalError" - << "UnicodeDecodeError" - << "UnicodeEncodeError" - << "UnicodeError" - << "UnicodeTranslateError" - << "ValueError" - << "WindowsError" - << "ZeroDivisionError" - << "Warning" - << "UserWarning" - << "DeprecationWarning" - << "PendingDeprecationWarning" - << "SyntaxWarning" - << "OverflowWarning" - << "RuntimeWarning" - << "FutureWarning"; - return aSpecialKeywords; + updateHighlight(); } void PyEditor_PyHighlighter::highlightBlock( const QString& theText ) @@ -353,3 +185,100 @@ void PyEditor_PyHighlighter::insertBracketsData( Brackets theBrackets, insertBracketsData( leftChar, rightChar, theData, theText ); } + +void PyEditor_PyHighlighter::onKeywordsChanged() +{ + updateHighlight(); + rehighlight(); +} + +void PyEditor_PyHighlighter::updateHighlight() +{ + highlightingRules.clear(); + + HighlightingRule aRule; + + QList dictList; + dictList << myStdKeywords << myUserKeywords; + + // Keywords + QSet existing; + for ( QList::const_iterator it = dictList.begin(); + it != dictList.end(); ++it ) { + PyEditor_Keywords* kwDict = *it; + QList colors = kwDict->colors(); + for ( QList::const_iterator itr = colors.begin(); itr != colors.end(); ++itr ) { + QColor color = *itr; + QTextCharFormat format; + format.setForeground( color ); + QStringList keywords = kwDict->keywords( color ); + foreach ( const QString& keyword, keywords ) { + if ( existing.contains( keyword ) ) + continue; + + aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) ); + aRule.format = format; + aRule.capture = 0; + highlightingRules.append( aRule ); + existing.insert( keyword ); + } + } + } + + // Reference to the current instance of the class + referenceClassFormat.setForeground( QColor( 179, 143, 0 ) ); + referenceClassFormat.setFontItalic( true ); + aRule.pattern = QRegExp( "\\bself\\b" ); + aRule.format = referenceClassFormat; + aRule.capture = 0; + highlightingRules.append( aRule ); + + // Numbers + numberFormat.setForeground( Qt::darkMagenta ); + aRule.pattern = QRegExp( "\\b([-+])?(\\d+(\\.)?\\d*|\\d*(\\.)?\\d+)(([eE]([-+])?)?\\d+)?\\b" ); + aRule.format = numberFormat; + aRule.capture = 0; + highlightingRules.append( aRule ); + + // String qoutation + quotationFormat.setForeground( Qt::darkGreen ); + aRule.pattern = QRegExp( "(?:'[^']*'|\"[^\"]*\")" ); + aRule.pattern.setMinimal( true ); + aRule.format = quotationFormat; + aRule.capture = 0; + highlightingRules.append( aRule ); + + // Function names + functionFormat.setFontWeight( QFont::Bold ); + aRule.pattern = QRegExp( "(?:def\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" ); + aRule.capture = 1; + aRule.format = functionFormat; + highlightingRules.append( aRule ); + + // Class names + classFormat.setForeground( Qt::darkBlue ); + classFormat.setFontWeight( QFont::Bold ); + aRule.pattern = QRegExp( "(?:class\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" ); + aRule.capture = 1; + aRule.format = classFormat; + highlightingRules.append( aRule ); + + // Multi line comments + multiLineCommentFormat.setForeground( Qt::darkRed ); + tripleQuotesExpression = QRegExp( "(:?\"[\"]\".*\"[\"]\"|'''.*''')" ); + aRule.pattern = tripleQuotesExpression; + aRule.pattern.setMinimal( true ); + aRule.format = multiLineCommentFormat; + aRule.capture = 0; + highlightingRules.append( aRule ); + + tripleSingleExpression = QRegExp( "'''(?!\")" ); + tripleDoubleExpression = QRegExp( "\"\"\"(?!')" ); + + // Single comments + singleLineCommentFormat.setForeground( Qt::darkGray ); + aRule.pattern = QRegExp( "#[^\n]*" ); + aRule.format = singleLineCommentFormat; + aRule.capture = 0; + highlightingRules.append( aRule ); +} diff --git a/tools/PyEditor/src/PyEditor_PyHighlighter.h b/tools/PyEditor/src/PyEditor_PyHighlighter.h index 961616fa2..07ac00611 100644 --- a/tools/PyEditor/src/PyEditor_PyHighlighter.h +++ b/tools/PyEditor/src/PyEditor_PyHighlighter.h @@ -26,6 +26,7 @@ #include class QTextDocument; +class PyEditor_Keywords; class PyEditor_PyHighlighter : public QSyntaxHighlighter { @@ -52,11 +53,14 @@ public: }; public: - PyEditor_PyHighlighter( QTextDocument* = 0 ); + PyEditor_PyHighlighter( QTextDocument*, + PyEditor_Keywords*, PyEditor_Keywords* ); - void initialize(); - QStringList keywords(); - QStringList specialKeywords(); +private Q_SLOTS: + void onKeywordsChanged(); + +protected: + void updateHighlight(); protected: struct HighlightingRule @@ -76,8 +80,6 @@ protected: QTextCharFormat classFormat; QTextCharFormat referenceClassFormat; QTextCharFormat functionFormat; - QTextCharFormat keywordFormat; - QTextCharFormat specialFromat; QTextCharFormat numberFormat; QTextCharFormat singleLineCommentFormat; QTextCharFormat multiLineCommentFormat; @@ -86,6 +88,10 @@ protected: void highlightBlock( const QString& ); void insertBracketsData( char, char, TextBlockData*, const QString& ); void insertBracketsData( Brackets, TextBlockData*, const QString& ); + +private: + PyEditor_Keywords* myStdKeywords; + PyEditor_Keywords* myUserKeywords; }; #endif // PYEDITOR_PYHIGHLIGHTER_H diff --git a/tools/PyEditor/src/PyEditor_Settings.cxx b/tools/PyEditor/src/PyEditor_Settings.cxx index d9fd52fd1..64a43d700 100644 --- a/tools/PyEditor/src/PyEditor_Settings.cxx +++ b/tools/PyEditor/src/PyEditor_Settings.cxx @@ -22,6 +22,8 @@ #include "PyEditor_Settings.h" +#include "PyEditor_Editor.h" + /*! \class PyEditor_Settings \brief Manager of setting values. @@ -61,7 +63,8 @@ PyEditor_Settings::PyEditor_Settings() myNumberColumns( 80 ), myTabSpaceVisible( true ), myTabSize( 4 ), - myFont( "Courier", 10 ) + myFont( "Courier", 10 ), + myCompletionPolicy( PyEditor_Editor::Always ) { } @@ -227,6 +230,24 @@ QFont PyEditor_Settings::font() const return myFont; } +/*! + \brief Set "completionPolicy" option. + \param completion policy option value +*/ +void PyEditor_Settings::setCompletionPolicy( int value ) +{ + myCompletionPolicy = value; +} + +/*! + \brief Get "completionPolicy" option. + \return option value +*/ +int PyEditor_Settings::completionPolicy() const +{ + return myCompletionPolicy; +} + /*! \brief Read settings from the persistence storage. Base implementation does nothing; it should be reimplemented in successors. @@ -258,6 +279,8 @@ void PyEditor_Settings::copyFrom( const PyEditor_Settings& other ) setVerticalEdge( other.verticalEdge() ); setNumberColumns( other.numberColumns() ); setFont( other.font() ); + setCompletionPolicy( other.completionPolicy() ); + save(); } @@ -278,6 +301,7 @@ QString PyEditor_Settings::option( Option option ) "TabSpaceVisible", "TabSize", "Font", + "CompletionPolicy" }; - return option >= 0 && option <= snFont ? options[option] : "Unknown"; + return option >= 0 && option <= snCompletionPolicy ? options[option] : "Unknown"; } diff --git a/tools/PyEditor/src/PyEditor_Settings.h b/tools/PyEditor/src/PyEditor_Settings.h index aa6de0419..7b7e4b7c2 100644 --- a/tools/PyEditor/src/PyEditor_Settings.h +++ b/tools/PyEditor/src/PyEditor_Settings.h @@ -39,7 +39,8 @@ protected: snNumberColumns, snTabSpaceVisible, snTabSize, - snFont }; + snFont, + snCompletionPolicy }; public: static PyEditor_Settings* settings(); @@ -75,6 +76,9 @@ public: void setFont( const QFont& ); QFont font() const; + void setCompletionPolicy( int ); + int completionPolicy() const; + virtual void load(); virtual void save(); @@ -101,6 +105,9 @@ private: // Font settings QFont myFont; + // Completion settings + int myCompletionPolicy; + static PyEditor_Settings* myGlobalSettings; }; diff --git a/tools/PyEditor/src/PyEditor_SettingsDlg.cxx b/tools/PyEditor/src/PyEditor_SettingsDlg.cxx index fc2c94b64..4b021a070 100644 --- a/tools/PyEditor/src/PyEditor_SettingsDlg.cxx +++ b/tools/PyEditor/src/PyEditor_SettingsDlg.cxx @@ -85,6 +85,16 @@ PyEditor_SettingsDlg::PyEditor_SettingsDlg( PyEditor_Editor* theEditor, aMainLayout->addWidget( aDisplaySetBox ); // . Display settings + // . Editor settings + QGroupBox* aEditorSetBox = new QGroupBox( tr( "GR_EDIT_SET" ), this ); + QGridLayout* aEditorSetLayout = new QGridLayout( aEditorSetBox ); + aEditorSetLayout->addWidget( new QLabel( tr( "LBL_COMPLETION_MODE" ), aEditorSetBox ), 0, 0 ); + aEditorSetLayout->addWidget( myCompletionPolicy = new QComboBox( aEditorSetBox ), 0, 1 ); + myCompletionPolicy->addItems( QStringList() << tr( "LBL_NONE" ) << tr( "LBL_AUTO" ) + << tr( "LBL_MANUAL" ) << tr( "LBL_ALWAYS" ) ); + aMainLayout->addWidget( aEditorSetBox ); + // . Editor settings + // . Tab settings QGroupBox* aTabSetBox = new QGroupBox( tr( "GR_TAB_SET" ), this ); QVBoxLayout* aTabSetLayout = new QVBoxLayout( aTabSetBox ); @@ -237,6 +247,7 @@ void PyEditor_SettingsDlg::settingsFromGui() settings.setVerticalEdge( myVerticalEdge->isChecked() ); settings.setNumberColumns( myNumberColumns->value() ); settings.setFont( font ); + settings.setCompletionPolicy( myCompletionPolicy->currentIndex() ); myEditor->setSettings(settings); // updateContent() PyEditor_Settings* globals = PyEditor_Settings::settings(); @@ -261,6 +272,7 @@ void PyEditor_SettingsDlg::settingsToGui() myNumberColumns->setValue( settings.numberColumns() ); myFontFamily->setCurrentFont( settings.font() ); setFontSize( QString::number( settings.font().pointSize() ) ); + myCompletionPolicy->setCurrentIndex( settings.completionPolicy() ); onVerticalEdgeChecked(); onFontChanged(); diff --git a/tools/PyEditor/src/PyEditor_SettingsDlg.h b/tools/PyEditor/src/PyEditor_SettingsDlg.h index 9f3dfa986..24e4c6768 100644 --- a/tools/PyEditor/src/PyEditor_SettingsDlg.h +++ b/tools/PyEditor/src/PyEditor_SettingsDlg.h @@ -39,7 +39,7 @@ class PYEDITOR_EXPORT PyEditor_SettingsDlg : public QDialog public: PyEditor_SettingsDlg( PyEditor_Editor*, bool = false, QWidget* = 0 ); - ~PyEditor_SettingsDlg(); + virtual ~PyEditor_SettingsDlg(); private Q_SLOTS: void onVerticalEdgeChecked(); @@ -71,6 +71,8 @@ private: QFontComboBox* myFontFamily; QComboBox* myFontSize; + QComboBox* myCompletionPolicy; + QCheckBox* myDefaultCheck; PyEditor_Editor* myEditor; diff --git a/tools/PyEditor/src/PyEditor_StdSettings.cxx b/tools/PyEditor/src/PyEditor_StdSettings.cxx index 816db3653..06a2b68eb 100644 --- a/tools/PyEditor/src/PyEditor_StdSettings.cxx +++ b/tools/PyEditor/src/PyEditor_StdSettings.cxx @@ -59,15 +59,24 @@ void PyEditor_StdSettings::load() { mySettings.beginGroup( myGroup.isEmpty() ? option( snEditor ) : myGroup ); - setHighlightCurrentLine( mySettings.value( option( snHighlightCurrentLine ), highlightCurrentLine() ).toBool() ); - setTextWrapping( mySettings.value( option( snTextWrapping ), textWrapping() ).toBool() ); - setCenterCursorOnScroll( mySettings.value( option( snCenterCursorOnScroll ), centerCursorOnScroll() ).toBool() ); - setLineNumberArea( mySettings.value( option( snLineNumberArea ), lineNumberArea() ).toBool() ); - setVerticalEdge( mySettings.value( option( snVerticalEdge ), verticalEdge() ).toBool() ); - setNumberColumns( mySettings.value( option( snNumberColumns ), numberColumns() ).toInt() ); - setTabSpaceVisible( mySettings.value( option( snTabSpaceVisible ), tabSpaceVisible() ).toBool() ); + setHighlightCurrentLine( mySettings.value( option( snHighlightCurrentLine ), + highlightCurrentLine() ).toBool() ); + setTextWrapping( mySettings.value( option( snTextWrapping ), + textWrapping() ).toBool() ); + setCenterCursorOnScroll( mySettings.value( option( snCenterCursorOnScroll ), + centerCursorOnScroll() ).toBool() ); + setLineNumberArea( mySettings.value( option( snLineNumberArea ), + lineNumberArea() ).toBool() ); + setVerticalEdge( mySettings.value( option( snVerticalEdge ), + verticalEdge() ).toBool() ); + setNumberColumns( mySettings.value( option( snNumberColumns ), + numberColumns() ).toInt() ); + setTabSpaceVisible( mySettings.value( option( snTabSpaceVisible ), + tabSpaceVisible() ).toBool() ); setTabSize( mySettings.value( option( snTabSize ), tabSize() ).toInt() ); setFont( mySettings.value( option( snFont ), font() ).value() ); + setCompletionPolicy( mySettings.value( option( snCompletionPolicy ), + completionPolicy() ).toInt() ); setLanguage( mySettings.value( "language", language() ).toString() ); mySettings.endGroup(); @@ -86,6 +95,7 @@ void PyEditor_StdSettings::save() mySettings.setValue( option( snTabSpaceVisible ), tabSpaceVisible() ); mySettings.setValue( option( snTabSize ), tabSize() ); mySettings.setValue( option( snFont ), font() ); + mySettings.setValue( option( snCompletionPolicy ), completionPolicy() ); mySettings.setValue( "language", language() ); mySettings.endGroup(); diff --git a/tools/PyEditor/src/python/PyEditorPy.sip b/tools/PyEditor/src/python/PyEditorPy.sip index 372cc2898..499ee9574 100644 --- a/tools/PyEditor/src/python/PyEditorPy.sip +++ b/tools/PyEditor/src/python/PyEditorPy.sip @@ -66,6 +66,9 @@ public: void setFont( const QFont& ); QFont font() const; + + void setCompletionPolicy( int ); + int completionPolicy() const; }; class PyEditor_Editor : QPlainTextEdit @@ -82,6 +85,10 @@ public: const PyEditor_Settings& settings() const; QString text() const; + QStringList keywords() const; + void appendKeywords( const QStringList&, int, const QColor& = QColor() ); + void removeKeywords( const QStringList& ); + public slots: void deleteSelected(); void append( const QString& ); diff --git a/tools/PyEditor/src/resources/about.txt b/tools/PyEditor/src/resources/about.txt index aeb24ff7c..75e476a52 100644 --- a/tools/PyEditor/src/resources/about.txt +++ b/tools/PyEditor/src/resources/about.txt @@ -30,6 +30,7 @@ The behavior of the editor can be customized via the Preferences dialog.
  • Enable text wrapping: allows wrapping text at the border of the editor's window.
  • Center cursor on scroll: allows scrolling the script vertically to make the cursor visible at the center of the viewer.
  • Display line numbers area: shows line numbers at the left border of the editor.
  • +
  • Completion mode: select the completion mode for inputted keywords.
  • Vertical edge settings: draws vertical line at the specified column of the viewer.
  • Display tab delimiters: displays tab marks at a given number of white spaces.
  • Save settings as default: saves chosen options as a default ones. These settings will be restored after application restart.
  • diff --git a/tools/PyEditor/src/resources/translations/PyEditor_msg_en.ts b/tools/PyEditor/src/resources/translations/PyEditor_msg_en.ts index c32adddb2..d1a9f40ad 100644 --- a/tools/PyEditor/src/resources/translations/PyEditor_msg_en.ts +++ b/tools/PyEditor/src/resources/translations/PyEditor_msg_en.ts @@ -31,6 +31,30 @@ LBL_LINE_NUMBS_AREA Display line numbers area + + GR_EDIT_SET + Editor settings + + + LBL_COMPLETION_MODE + Completion mode + + + LBL_NONE + None + + + LBL_AUTO + Auto + + + LBL_MANUAL + Manual + + + LBL_ALWAYS + Always + GR_TAB_SET Tab settings diff --git a/tools/PyEditor/src/resources/translations/PyEditor_msg_fr.ts b/tools/PyEditor/src/resources/translations/PyEditor_msg_fr.ts index 96cd1e001..17aad7262 100644 --- a/tools/PyEditor/src/resources/translations/PyEditor_msg_fr.ts +++ b/tools/PyEditor/src/resources/translations/PyEditor_msg_fr.ts @@ -31,6 +31,30 @@ LBL_LINE_NUMBS_AREA Affiche les numéros de ligne + + GR_EDIT_SET + Editor settings + + + LBL_COMPLETION_MODE + Completion mode + + + LBL_NONE + None + + + LBL_AUTO + Auto + + + LBL_MANUAL + Manual + + + LBL_ALWAYS + Always + GR_TAB_SET Indentation diff --git a/tools/PyEditor/src/resources/translations/PyEditor_msg_ja.ts b/tools/PyEditor/src/resources/translations/PyEditor_msg_ja.ts index e77eaa134..199ba7d46 100644 --- a/tools/PyEditor/src/resources/translations/PyEditor_msg_ja.ts +++ b/tools/PyEditor/src/resources/translations/PyEditor_msg_ja.ts @@ -31,6 +31,30 @@ LBL_LINE_NUMBS_AREA ライン番号エリアの表示 + + GR_EDIT_SET + Editor settings + + + LBL_COMPLETION_MODE + Completion mode + + + LBL_NONE + None + + + LBL_AUTO + Auto + + + LBL_MANUAL + Manual + + + LBL_ALWAYS + Always + GR_TAB_SET タブ設定 -- 2.39.2