Skip to content

Commit

Permalink
Move python console history handling to base QgsCodeEditor class
Browse files Browse the repository at this point in the history
So that other non-python-console code editors can utilise this too
  • Loading branch information
nyalldawson committed Oct 14, 2022
1 parent 753efc2 commit 1f9de59
Show file tree
Hide file tree
Showing 18 changed files with 630 additions and 293 deletions.
2 changes: 1 addition & 1 deletion python/console/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ def saveSettingsConsole(self):
self.settings.setValue("pythonConsole/splitterObj", self.splitterObj.saveState())
self.settings.setValue("pythonConsole/splitterEditor", self.splitterEditor.saveState())

self.shell.writeHistoryFile(True)
self.shell.writeHistoryFile()

def restoreSettingsConsole(self):
storedTabScripts = self.settings.value("pythonConsole/tabScripts", [])
Expand Down
321 changes: 50 additions & 271 deletions python/console/console_sci.py

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions python/gui/auto_generated/codeeditors/qgscodeeditor.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,32 @@ Returns ``True`` if the cursor is on the last line of the document.
.. versionadded:: 3.28
%End

void setHistoryFilePath( const QString &path );
%Docstring
Sets the file path to use for recording and retrieving previously
executed commands.

.. note::

Applies to code editors in the QgsCodeEditor.Mode.CommandInput mode only.

.. versionadded:: 3.30
%End


QStringList history() const;

public slots:

virtual void runCommand( const QString &command );
%Docstring
Runs a command in the editor.

The base class method does nothing.

.. versionadded:: 3.30
%End

virtual void moveCursorToStart();
%Docstring
Moves the cursor to the start of the document and scrolls to ensure
Expand All @@ -245,6 +269,53 @@ it is visible.
.. versionadded:: 3.28
%End

void showPreviousCommand();
%Docstring
Shows the previous command from the session in the editor.

.. note::

Applies to code editors in the QgsCodeEditor.Mode.CommandInput mode only.

.. versionadded:: 3.30
%End

void showNextCommand();
%Docstring
Shows the next command from the session in the editor.

.. note::

Applies to code editors in the QgsCodeEditor.Mode.CommandInput mode only.

.. versionadded:: 3.30
%End

void showHistory();

void removeHistoryCommand( int index );

void clearSessionHistory();
%Docstring
Clears the history of commands run in the current session.

.. note::

Applies to code editors in the QgsCodeEditor.Mode.CommandInput mode only.

.. versionadded:: 3.30
%End


void clearPersistentHistory();

bool writeHistoryFile();

signals:

void sessionHistoryCleared();
void persistentHistoryCleared();

protected:

bool isFixedPitch( const QFont &font );
Expand All @@ -253,6 +324,8 @@ it is visible.

virtual void keyPressEvent( QKeyEvent *event );

virtual void contextMenuEvent( QContextMenuEvent *event );


virtual void initializeLexer();
%Docstring
Expand Down Expand Up @@ -284,6 +357,13 @@ Performs tasks which must be run after a lexer has been set for the widget.
.. versionadded:: 3.16
%End


void syncSoftHistory();
void updateSoftHistory();
void updateHistory( const QStringList &commands, bool skipSoftHistory = false );

virtual void populateContextMenu( QMenu *menu );

};

QFlags<QgsCodeEditor::Flag> operator|(QgsCodeEditor::Flag f1, QFlags<QgsCodeEditor::Flag> f2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ code autocompletion.
%End
public:

QgsCodeEditorPython( QWidget *parent /TransferThis/ = 0, const QList<QString> &filenames = QList<QString>() );
QgsCodeEditorPython( QWidget *parent /TransferThis/ = 0, const QList<QString> &filenames = QList<QString>(),
QgsCodeEditor::Mode mode = QgsCodeEditor::Mode::ScriptEditor );
%Docstring
Construct a new Python editor.

Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ set(QGIS_GUI_SRCS
codeeditors/qgscodeeditorcolorscheme.cpp
codeeditors/qgscodeeditorcolorschemeregistry.cpp
codeeditors/qgscodeeditorcss.cpp
codeeditors/qgscodeeditorhistorydialog.cpp
codeeditors/qgscodeeditorhtml.cpp
codeeditors/qgscodeeditorjs.cpp
codeeditors/qgscodeeditorjson.cpp
Expand Down Expand Up @@ -998,6 +999,7 @@ set(QGIS_GUI_HDRS
codeeditors/qgscodeeditorcolorschemeregistry.h
codeeditors/qgscodeeditorcss.h
codeeditors/qgscodeeditorexpression.h
codeeditors/qgscodeeditorhistorydialog.h
codeeditors/qgscodeeditorhtml.h
codeeditors/qgscodeeditorjs.h
codeeditors/qgscodeeditorjson.h
Expand Down
195 changes: 195 additions & 0 deletions src/gui/codeeditors/qgscodeeditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "qgssymbollayerutils.h"
#include "qgsgui.h"
#include "qgscodeeditorcolorschemeregistry.h"
#include "qgscodeeditorhistorydialog.h"

#include <QLabel>
#include <QWidget>
Expand All @@ -28,6 +29,8 @@
#include <QDebug>
#include <QFocusEvent>
#include <Qsci/qscistyle.h>
#include <QMenu>
#include <QClipboard>

QMap< QgsCodeEditorColorScheme::ColorRole, QString > QgsCodeEditor::sColorRoleToSettingsKey
{
Expand Down Expand Up @@ -88,6 +91,8 @@ QgsCodeEditor::QgsCodeEditor( QWidget *parent, const QString &title, bool foldin
if ( folding )
mFlags |= QgsCodeEditor::Flag::CodeFolding;

mSoftHistory.append( QString() );

setSciWidget();
setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );

Expand Down Expand Up @@ -176,6 +181,34 @@ void QgsCodeEditor::keyPressEvent( QKeyEvent *event )
}
}

void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
{
if ( mMode != QgsCodeEditor::Mode::CommandInput )
{
QsciScintilla::contextMenuEvent( event );
return;
}

QMenu *menu = new QMenu( this );
QMenu *historySubMenu = new QMenu( tr( "Command History" ), menu );

historySubMenu->addAction( tr( "Show" ), this, &QgsCodeEditor::showHistory, QStringLiteral( "Ctrl+Shift+SPACE" ) );
historySubMenu->addAction( tr( "Clear File" ), this, &QgsCodeEditor::clearPersistentHistory );
historySubMenu->addAction( tr( "Clear Session" ), this, &QgsCodeEditor::clearSessionHistory );

menu->addMenu( historySubMenu );
menu->addSeparator();

QAction *copyAction = menu->addAction( QgsApplication::getThemeIcon( "mActionEditCopy.svg" ), tr( "Copy" ), this, &QgsCodeEditor::copy, QKeySequence::Copy );
QAction *pasteAction = menu->addAction( QgsApplication::getThemeIcon( "mActionEditPaste.svg" ), tr( "Paste" ), this, &QgsCodeEditor::paste, QKeySequence::Paste );
copyAction->setEnabled( hasSelectedText() );
pasteAction->setEnabled( !QApplication::clipboard()->text().isEmpty() );

populateContextMenu( menu );

menu->exec( mapToGlobal( event->pos() ) );
}

void QgsCodeEditor::initializeLexer()
{

Expand Down Expand Up @@ -406,6 +439,162 @@ void QgsCodeEditor::updateFolding()
}
}

bool QgsCodeEditor::readHistoryFile()
{
if ( mHistoryFilePath.isEmpty() || !QFile::exists( mHistoryFilePath ) )
return false;

QFile file( mHistoryFilePath );
if ( file.open( QIODevice::ReadOnly ) )
{
QTextStream stream( &file );
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// Always use UTF-8
stream.setCodec( "UTF-8" );
#endif
QString line;
while ( !stream.atEnd() )
{
line = stream.readLine(); // line of text excluding '\n'
mHistory.append( line );
}
syncSoftHistory();
return true;
}

return false;
}

void QgsCodeEditor::syncSoftHistory()
{
mSoftHistory = mHistory;
mSoftHistory.append( QString() );
mSoftHistoryIndex = mSoftHistory.length() - 1;
}

void QgsCodeEditor::updateSoftHistory()
{
mSoftHistory[mSoftHistoryIndex] = text();
}

void QgsCodeEditor::updateHistory( const QStringList &commands, bool skipSoftHistory )
{
if ( commands.size() > 1 )
{
mHistory.append( commands );
}
else if ( !commands.value( 0 ).isEmpty() )
{
const QString command = commands.value( 0 );
if ( mHistory.empty() || command != mHistory.constLast() )
mHistory.append( command );
}

if ( !skipSoftHistory )
syncSoftHistory();
}

void QgsCodeEditor::populateContextMenu( QMenu * )
{

}

QStringList QgsCodeEditor::history() const
{
return mHistory;
}

void QgsCodeEditor::runCommand( const QString & )
{

}

void QgsCodeEditor::clearSessionHistory()
{
mHistory.clear();
readHistoryFile();
syncSoftHistory();

emit sessionHistoryCleared();
}

void QgsCodeEditor::clearPersistentHistory()
{
mHistory.clear();

if ( !mHistoryFilePath.isEmpty() && QFile::exists( mHistoryFilePath ) )
{
QFile file( mHistoryFilePath );
file.open( QFile::WriteOnly | QFile::Truncate );
}

emit persistentHistoryCleared();
}

bool QgsCodeEditor::writeHistoryFile()
{
if ( mHistoryFilePath.isEmpty() )
return false;

QFile f( mHistoryFilePath );
if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
{
return false;
}

QTextStream ts( &f );
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
ts.setCodec( "UTF-8" );
#endif
for ( const QString &command : std::as_const( mHistory ) )
{
ts << command + '\n';
}
return true;
}

void QgsCodeEditor::showPreviousCommand()
{
if ( mSoftHistoryIndex < mSoftHistory.length() - 1 && !mSoftHistory.isEmpty() )
{
mSoftHistoryIndex += 1;
setText( mSoftHistory[mSoftHistoryIndex] );
moveCursorToEnd();
}
}

void QgsCodeEditor::showNextCommand()
{
if ( mSoftHistoryIndex > 0 && !mSoftHistory.empty() )
{
mSoftHistoryIndex -= 1;
setText( mSoftHistory[mSoftHistoryIndex] );
moveCursorToEnd();
}
}

void QgsCodeEditor::showHistory()
{
QgsCodeEditorHistoryDialog *dialog = new QgsCodeEditorHistoryDialog( this, this );
dialog->setAttribute( Qt::WA_DeleteOnClose );

dialog->show();
dialog->activateWindow();
}

void QgsCodeEditor::removeHistoryCommand( int index )
{
// remove item from the command history (just for the current session)
mHistory.removeAt( index );
mSoftHistory.removeAt( index );
if ( index < mSoftHistoryIndex )
{
mSoftHistoryIndex -= 1;
if ( mSoftHistoryIndex < 0 )
mSoftHistoryIndex = mSoftHistory.length() - 1;
}
}

void QgsCodeEditor::insertText( const QString &text )
{
// Insert the text or replace selected text
Expand Down Expand Up @@ -604,6 +793,12 @@ bool QgsCodeEditor::isCursorOnLastLine() const
return line == lines() - 1;
}

void QgsCodeEditor::setHistoryFilePath( const QString &path )
{
mHistoryFilePath = path;
readHistoryFile();
}

void QgsCodeEditor::moveCursorToStart()
{
setCursorPosition( 0, 0 );
Expand Down
Loading

0 comments on commit 1f9de59

Please sign in to comment.