mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-09 00:08:52 -04:00
1036 lines
35 KiB
C++
1036 lines
35 KiB
C++
/***************************************************************************
|
|
qgscodeeditor.cpp - A base code editor for QGIS and plugins. Provides
|
|
a base editor using QScintilla for editors
|
|
--------------------------------------
|
|
Date : 06-Oct-2013
|
|
Copyright : (C) 2013 by Salvatore Larosa
|
|
Email : lrssvtml (at) gmail (dot) com
|
|
***************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "qgsapplication.h"
|
|
#include "qgscodeeditor.h"
|
|
#include "qgssettings.h"
|
|
#include "qgssymbollayerutils.h"
|
|
#include "qgsgui.h"
|
|
#include "qgscodeeditorcolorschemeregistry.h"
|
|
#include "qgscodeeditorhistorydialog.h"
|
|
#include "qgsstringutils.h"
|
|
|
|
#include <QLabel>
|
|
#include <QWidget>
|
|
#include <QFont>
|
|
#include <QFontDatabase>
|
|
#include <QDebug>
|
|
#include <QFocusEvent>
|
|
#include <Qsci/qscistyle.h>
|
|
#include <QMenu>
|
|
#include <QClipboard>
|
|
#include <QScrollBar>
|
|
|
|
QMap< QgsCodeEditorColorScheme::ColorRole, QString > QgsCodeEditor::sColorRoleToSettingsKey
|
|
{
|
|
{QgsCodeEditorColorScheme::ColorRole::Default, QStringLiteral( "defaultFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Keyword, QStringLiteral( "keywordFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Class, QStringLiteral( "classFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Method, QStringLiteral( "methodFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Decoration, QStringLiteral( "decoratorFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Number, QStringLiteral( "numberFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Comment, QStringLiteral( "commentFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::CommentLine, QStringLiteral( "commentLineFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::CommentBlock, QStringLiteral( "commentBlockFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Background, QStringLiteral( "paperBackgroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Cursor, QStringLiteral( "cursorColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::CaretLine, QStringLiteral( "caretLineColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Operator, QStringLiteral( "operatorFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::QuotedOperator, QStringLiteral( "quotedOperatorFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Identifier, QStringLiteral( "identifierFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::QuotedIdentifier, QStringLiteral( "quotedIdentifierFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Tag, QStringLiteral( "tagFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::UnknownTag, QStringLiteral( "unknownTagFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::SingleQuote, QStringLiteral( "singleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::DoubleQuote, QStringLiteral( "doubleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::TripleSingleQuote, QStringLiteral( "tripleSingleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::TripleDoubleQuote, QStringLiteral( "tripleDoubleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MarginBackground, QStringLiteral( "marginBackgroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MarginForeground, QStringLiteral( "marginForegroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::SelectionBackground, QStringLiteral( "selectionBackgroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::SelectionForeground, QStringLiteral( "selectionForegroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MatchedBraceBackground, QStringLiteral( "matchedBraceBackground" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MatchedBraceForeground, QStringLiteral( "matchedBraceColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Edge, QStringLiteral( "edgeColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Fold, QStringLiteral( "foldColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Error, QStringLiteral( "stderrFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::ErrorBackground, QStringLiteral( "stderrBackgroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::FoldIconForeground, QStringLiteral( "foldIconForeground" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::FoldIconHalo, QStringLiteral( "foldIconHalo" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::IndentationGuide, QStringLiteral( "indentationGuide" ) },
|
|
};
|
|
|
|
|
|
QgsCodeEditor::QgsCodeEditor( QWidget *parent, const QString &title, bool folding, bool margin, QgsCodeEditor::Flags flags, QgsCodeEditor::Mode mode )
|
|
: QsciScintilla( parent )
|
|
, mWidgetTitle( title )
|
|
, mMargin( margin )
|
|
, mFlags( flags )
|
|
, mMode( mode )
|
|
{
|
|
if ( !parent && mWidgetTitle.isEmpty() )
|
|
{
|
|
setWindowTitle( QStringLiteral( "Text Editor" ) );
|
|
}
|
|
else
|
|
{
|
|
setWindowTitle( mWidgetTitle );
|
|
}
|
|
|
|
if ( folding )
|
|
mFlags |= QgsCodeEditor::Flag::CodeFolding;
|
|
|
|
mSoftHistory.append( QString() );
|
|
|
|
setSciWidget();
|
|
setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
|
|
|
|
SendScintilla( SCI_SETADDITIONALSELECTIONTYPING, 1 );
|
|
SendScintilla( SCI_SETMULTIPASTE, 1 );
|
|
SendScintilla( SCI_SETVIRTUALSPACEOPTIONS, SCVS_RECTANGULARSELECTION );
|
|
|
|
SendScintilla( SCI_SETMARGINTYPEN, static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), SC_MARGIN_SYMBOL );
|
|
SendScintilla( SCI_SETMARGINMASKN, static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 1 << MARKER_NUMBER );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
|
|
setAnnotationDisplay( QsciScintilla::AnnotationBoxed );
|
|
|
|
connect( QgsGui::instance(), &QgsGui::optionsChanged, this, [ = ]
|
|
{
|
|
setSciWidget();
|
|
initializeLexer();
|
|
} );
|
|
|
|
switch ( mMode )
|
|
{
|
|
case QgsCodeEditor::Mode::ScriptEditor:
|
|
break;
|
|
|
|
case QgsCodeEditor::Mode::OutputDisplay:
|
|
{
|
|
// Don't want to see the horizontal scrollbar at all
|
|
SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
|
|
|
|
setWrapMode( QsciScintilla::WrapCharacter );
|
|
break;
|
|
}
|
|
|
|
case QgsCodeEditor::Mode::CommandInput:
|
|
{
|
|
// Don't want to see the horizontal scrollbar at all
|
|
SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
|
|
|
|
setWrapMode( QsciScintilla::WrapCharacter );
|
|
SendScintilla( QsciScintilla::SCI_EMPTYUNDOBUFFER );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Workaround a bug in QScintilla 2.8.X
|
|
void QgsCodeEditor::focusOutEvent( QFocusEvent *event )
|
|
{
|
|
#if QSCINTILLA_VERSION >= 0x020800 && QSCINTILLA_VERSION < 0x020900
|
|
if ( event->reason() != Qt::ActiveWindowFocusReason )
|
|
{
|
|
/* There's a bug in all QScintilla 2.8.X, where
|
|
a focus out event that is not due to ActiveWindowFocusReason doesn't
|
|
lead to the bliking caret being disabled. The hack consists in making
|
|
QsciScintilla::focusOutEvent believe that the event is a ActiveWindowFocusReason
|
|
The bug was fixed in 2.9 per:
|
|
2015-04-14 Phil Thompson <phil@riverbankcomputing.com>
|
|
|
|
* qt/qsciscintillabase.cpp:
|
|
Fixed a problem notifying when focus is lost to another application
|
|
widget.
|
|
[41734678234e]
|
|
*/
|
|
QFocusEvent newFocusEvent( QEvent::FocusOut, Qt::ActiveWindowFocusReason );
|
|
QsciScintilla::focusOutEvent( &newFocusEvent );
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
QsciScintilla::focusOutEvent( event );
|
|
}
|
|
}
|
|
|
|
// This workaround a likely bug in QScintilla. The ESC key should not be consumned
|
|
// by the main entry, so that the default behavior (Dialog closing) can trigger,
|
|
// but only is the auto-completion suggestion list isn't displayed
|
|
void QgsCodeEditor::keyPressEvent( QKeyEvent *event )
|
|
{
|
|
if ( isListActive() )
|
|
{
|
|
QsciScintilla::keyPressEvent( event );
|
|
return;
|
|
}
|
|
|
|
if ( event->key() == Qt::Key_Escape )
|
|
{
|
|
// Shortcut QScintilla and redirect the event to the QWidget handler
|
|
QWidget::keyPressEvent( event ); // NOLINT(bugprone-parent-virtual-call) clazy:exclude=skipped-base-method
|
|
return;
|
|
}
|
|
|
|
if ( mMode == QgsCodeEditor::Mode::CommandInput )
|
|
{
|
|
switch ( event->key() )
|
|
{
|
|
case Qt::Key_Return:
|
|
case Qt::Key_Enter:
|
|
runCommand( text() );
|
|
updatePrompt();
|
|
return;
|
|
|
|
case Qt::Key_Down:
|
|
showPreviousCommand();
|
|
updatePrompt();
|
|
return;
|
|
|
|
case Qt::Key_Up:
|
|
showNextCommand();
|
|
updatePrompt();
|
|
return;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
QsciScintilla::keyPressEvent( event );
|
|
|
|
}
|
|
|
|
void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
|
|
{
|
|
switch ( mMode )
|
|
{
|
|
case Mode::ScriptEditor:
|
|
{
|
|
QMenu *menu = createStandardContextMenu();
|
|
menu->setAttribute( Qt::WA_DeleteOnClose );
|
|
|
|
if ( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat )
|
|
{
|
|
menu->addSeparator();
|
|
|
|
QAction *reformatAction = new QAction( tr( "Reformat Code" ), menu );
|
|
reformatAction->setEnabled( !isReadOnly() );
|
|
connect( reformatAction, &QAction::triggered, this, &QgsCodeEditor::reformatCode );
|
|
menu->addAction( reformatAction );
|
|
}
|
|
|
|
populateContextMenu( menu );
|
|
|
|
menu->exec( mapToGlobal( event->pos() ) );
|
|
break;
|
|
}
|
|
|
|
case Mode::CommandInput:
|
|
{
|
|
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() ) );
|
|
break;
|
|
}
|
|
|
|
case Mode::OutputDisplay:
|
|
QsciScintilla::contextMenuEvent( event );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QgsCodeEditor::initializeLexer()
|
|
{
|
|
|
|
}
|
|
|
|
QColor QgsCodeEditor::lexerColor( QgsCodeEditorColorScheme::ColorRole role ) const
|
|
{
|
|
if ( mUseDefaultSettings )
|
|
return color( role );
|
|
|
|
if ( !mOverrideColors )
|
|
{
|
|
return defaultColor( role, mColorScheme );
|
|
}
|
|
else
|
|
{
|
|
const QColor color = mCustomColors.value( role );
|
|
return !color.isValid() ? defaultColor( role ) : color;
|
|
}
|
|
}
|
|
|
|
QFont QgsCodeEditor::lexerFont() const
|
|
{
|
|
if ( mUseDefaultSettings )
|
|
return getMonospaceFont();
|
|
|
|
QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
|
|
|
|
const QgsSettings settings;
|
|
if ( !mFontFamily.isEmpty() )
|
|
font.setFamily( mFontFamily );
|
|
|
|
#ifdef Q_OS_MAC
|
|
if ( mFontSize > 0 )
|
|
font.setPointSize( mFontSize );
|
|
else
|
|
{
|
|
// The font size gotten from getMonospaceFont() is too small on Mac
|
|
font.setPointSize( QLabel().font().pointSize() );
|
|
}
|
|
#else
|
|
if ( mFontSize > 0 )
|
|
font.setPointSize( mFontSize );
|
|
else
|
|
{
|
|
const int fontSize = settings.value( QStringLiteral( "qgis/stylesheet/fontPointSize" ), 10 ).toInt();
|
|
font.setPointSize( fontSize );
|
|
}
|
|
#endif
|
|
font.setBold( false );
|
|
|
|
return font;
|
|
}
|
|
|
|
void QgsCodeEditor::runPostLexerConfigurationTasks()
|
|
{
|
|
updateFolding();
|
|
|
|
setMatchedBraceForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MatchedBraceForeground ) );
|
|
setMatchedBraceBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MatchedBraceBackground ) );
|
|
|
|
SendScintilla( SCI_MARKERSETFORE, SC_MARKNUM_FOLDEROPEN, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconHalo ) );
|
|
SendScintilla( SCI_MARKERSETBACK, SC_MARKNUM_FOLDEROPEN, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconForeground ) );
|
|
SendScintilla( SCI_MARKERSETFORE, SC_MARKNUM_FOLDER, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconHalo ) );
|
|
SendScintilla( SCI_MARKERSETBACK, SC_MARKNUM_FOLDER, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconForeground ) );
|
|
SendScintilla( SCI_STYLESETFORE, STYLE_INDENTGUIDE, lexerColor( QgsCodeEditorColorScheme::ColorRole::IndentationGuide ) );
|
|
SendScintilla( SCI_STYLESETBACK, STYLE_INDENTGUIDE, lexerColor( QgsCodeEditorColorScheme::ColorRole::IndentationGuide ) );
|
|
|
|
if ( mMode == QgsCodeEditor::Mode::CommandInput )
|
|
{
|
|
setCaretLineVisible( false );
|
|
setLineNumbersVisible( false ); // NO linenumbers for the input line
|
|
// Margin 1 is used for the '>' prompt (console input)
|
|
setMarginLineNumbers( 1, true );
|
|
setMarginWidth( 1, "00000" );
|
|
setMarginType( 1, QsciScintilla::MarginType::TextMarginRightJustified );
|
|
setMarginsBackgroundColor( color( QgsCodeEditorColorScheme::ColorRole::Background ) );
|
|
setEdgeMode( QsciScintilla::EdgeNone );
|
|
}
|
|
}
|
|
|
|
void QgsCodeEditor::setSciWidget()
|
|
{
|
|
const QFont font = lexerFont();
|
|
setFont( font );
|
|
|
|
setUtf8( true );
|
|
setCaretLineVisible( true );
|
|
setCaretLineBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::CaretLine ) );
|
|
setCaretForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::Cursor ) );
|
|
setSelectionForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::SelectionForeground ) );
|
|
setSelectionBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::SelectionBackground ) );
|
|
|
|
setBraceMatching( QsciScintilla::SloppyBraceMatch );
|
|
setMatchedBraceForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MatchedBraceForeground ) );
|
|
setMatchedBraceBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MatchedBraceBackground ) );
|
|
|
|
setLineNumbersVisible( false );
|
|
|
|
// temporarily disable folding, will be enabled later if required by updateFolding()
|
|
setFolding( QsciScintilla::NoFoldStyle );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), 0 );
|
|
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
|
|
|
|
setMarginsForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginForeground ) );
|
|
setMarginsBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginBackground ) );
|
|
setIndentationGuidesForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginForeground ) );
|
|
setIndentationGuidesBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginBackground ) );
|
|
// whether margin will be shown
|
|
updateFolding();
|
|
const QColor foldColor = lexerColor( QgsCodeEditorColorScheme::ColorRole::Fold );
|
|
setFoldMarginColors( foldColor, foldColor );
|
|
// indentation
|
|
setAutoIndent( true );
|
|
setIndentationWidth( 4 );
|
|
setTabIndents( true );
|
|
setBackspaceUnindents( true );
|
|
setTabWidth( 4 );
|
|
// autocomplete
|
|
setAutoCompletionThreshold( 2 );
|
|
setAutoCompletionSource( QsciScintilla::AcsAPIs );
|
|
|
|
markerDefine( QgsApplication::getThemePixmap( "console/iconSyntaxErrorConsoleParams.svg", lexerColor( QgsCodeEditorColorScheme::ColorRole::Error ),
|
|
lexerColor( QgsCodeEditorColorScheme::ColorRole::ErrorBackground ), 16 ), MARKER_NUMBER );
|
|
}
|
|
|
|
void QgsCodeEditor::setTitle( const QString &title )
|
|
{
|
|
setWindowTitle( title );
|
|
}
|
|
|
|
Qgis::ScriptLanguage QgsCodeEditor::language() const
|
|
{
|
|
return Qgis::ScriptLanguage::Unknown;
|
|
}
|
|
|
|
Qgis::ScriptLanguageCapabilities QgsCodeEditor::languageCapabilities() const
|
|
{
|
|
return Qgis::ScriptLanguageCapabilities();
|
|
}
|
|
|
|
QString QgsCodeEditor::languageToString( Qgis::ScriptLanguage language )
|
|
{
|
|
switch ( language )
|
|
{
|
|
case Qgis::ScriptLanguage::Css:
|
|
return tr( "CSS" );
|
|
case Qgis::ScriptLanguage::QgisExpression:
|
|
return tr( "Expression" );
|
|
case Qgis::ScriptLanguage::Html:
|
|
return tr( "HTML" );
|
|
case Qgis::ScriptLanguage::JavaScript:
|
|
return tr( "JavaScript" );
|
|
case Qgis::ScriptLanguage::Json:
|
|
return tr( "JSON" );
|
|
case Qgis::ScriptLanguage::Python:
|
|
return tr( "Python" );
|
|
case Qgis::ScriptLanguage::R:
|
|
return tr( "R" );
|
|
case Qgis::ScriptLanguage::Sql:
|
|
return tr( "SQL" );
|
|
case Qgis::ScriptLanguage::Unknown:
|
|
return QString();
|
|
}
|
|
BUILTIN_UNREACHABLE
|
|
}
|
|
|
|
void QgsCodeEditor::setMarginVisible( bool margin )
|
|
{
|
|
mMargin = margin;
|
|
if ( margin )
|
|
{
|
|
QFont marginFont = lexerFont();
|
|
marginFont.setPointSize( 10 );
|
|
setMarginLineNumbers( 0, true );
|
|
setMarginsFont( marginFont );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), QStringLiteral( "00000" ) );
|
|
setMarginsForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginForeground ) );
|
|
setMarginsBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginBackground ) );
|
|
}
|
|
else
|
|
{
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), 0 );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), 0 );
|
|
}
|
|
}
|
|
|
|
void QgsCodeEditor::setLineNumbersVisible( bool visible )
|
|
{
|
|
if ( visible )
|
|
{
|
|
QFont marginFont = lexerFont();
|
|
marginFont.setPointSize( 10 );
|
|
setMarginLineNumbers( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), true );
|
|
setMarginsFont( marginFont );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), QStringLiteral( "00000" ) );
|
|
setMarginsForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginForeground ) );
|
|
setMarginsBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginBackground ) );
|
|
}
|
|
else
|
|
{
|
|
setMarginLineNumbers( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), false );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), 0 );
|
|
}
|
|
}
|
|
|
|
bool QgsCodeEditor::lineNumbersVisible() const
|
|
{
|
|
return marginLineNumbers( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ) );
|
|
}
|
|
|
|
void QgsCodeEditor::setFoldingVisible( bool folding )
|
|
{
|
|
if ( folding )
|
|
{
|
|
mFlags |= QgsCodeEditor::Flag::CodeFolding;
|
|
}
|
|
else
|
|
{
|
|
mFlags &= ~( static_cast< int >( QgsCodeEditor::Flag::CodeFolding ) );
|
|
}
|
|
updateFolding();
|
|
}
|
|
|
|
bool QgsCodeEditor::foldingVisible()
|
|
{
|
|
return mFlags & QgsCodeEditor::Flag::CodeFolding;
|
|
}
|
|
|
|
void QgsCodeEditor::updateFolding()
|
|
{
|
|
if ( ( mFlags & QgsCodeEditor::Flag::CodeFolding ) && mMode == QgsCodeEditor::Mode::ScriptEditor )
|
|
{
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), "0" );
|
|
setMarginsForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginForeground ) );
|
|
setMarginsBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginBackground ) );
|
|
setFolding( QsciScintilla::PlainFoldStyle );
|
|
}
|
|
else
|
|
{
|
|
setFolding( QsciScintilla::NoFoldStyle );
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), 0 );
|
|
}
|
|
}
|
|
|
|
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 * )
|
|
{
|
|
|
|
}
|
|
|
|
QString QgsCodeEditor::reformatCodeString( const QString &string, bool &ok, QString & ) const
|
|
{
|
|
ok = false;
|
|
return string;
|
|
}
|
|
|
|
void QgsCodeEditor::updatePrompt()
|
|
{
|
|
if ( mInterpreter )
|
|
{
|
|
const QString prompt = mInterpreter->promptForState( mInterpreter->currentState() );
|
|
SendScintilla( QsciScintilla::SCI_MARGINSETTEXT, static_cast< uintptr_t >( 0 ), prompt.toUtf8().constData() );
|
|
}
|
|
}
|
|
|
|
QgsCodeInterpreter *QgsCodeEditor::interpreter() const
|
|
{
|
|
return mInterpreter;
|
|
}
|
|
|
|
void QgsCodeEditor::setInterpreter( QgsCodeInterpreter *newInterpreter )
|
|
{
|
|
mInterpreter = newInterpreter;
|
|
updatePrompt();
|
|
}
|
|
|
|
// Find the source substring index that most closely matches the target string
|
|
int findMinimalDistanceIndex( const QString &source, const QString &target )
|
|
{
|
|
const int index = std::min( source.length(), target.length() );
|
|
|
|
const int d0 = QgsStringUtils::levenshteinDistance( source.left( index ), target );
|
|
if ( d0 == 0 )
|
|
return index;
|
|
|
|
int refDistanceMore = d0;
|
|
int refIndexMore = index;
|
|
if ( index < source.length() - 1 )
|
|
{
|
|
while ( true )
|
|
{
|
|
const int newDistance = QgsStringUtils::levenshteinDistance( source.left( refIndexMore + 1 ), target );
|
|
if ( newDistance <= refDistanceMore )
|
|
{
|
|
refDistanceMore = newDistance;
|
|
refIndexMore++;
|
|
if ( refIndexMore == source.length() - 1 )
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int refDistanceLess = d0;
|
|
int refIndexLess = index;
|
|
if ( index > 0 )
|
|
{
|
|
while ( true )
|
|
{
|
|
const int newDistance = QgsStringUtils::levenshteinDistance( source.left( refIndexLess - 1 ), target );
|
|
if ( newDistance <= refDistanceLess )
|
|
{
|
|
refDistanceLess = newDistance;
|
|
refIndexLess--;
|
|
if ( refIndexLess == 0 )
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( refDistanceMore < refDistanceLess )
|
|
return refIndexMore;
|
|
else
|
|
return refIndexLess;
|
|
}
|
|
|
|
void QgsCodeEditor::reformatCode()
|
|
{
|
|
if ( !( languageCapabilities() & Qgis::ScriptLanguageCapability::Reformat ) )
|
|
return;
|
|
|
|
int line = 0;
|
|
int index = 0;
|
|
getCursorPosition( &line, &index );
|
|
const QString textBeforeCursor = text( 0, positionFromLineIndex( line, index ) );
|
|
|
|
const QString originalText = text();
|
|
|
|
bool ok = false;
|
|
QString error;
|
|
const QString newText = reformatCodeString( originalText, ok, error );
|
|
if ( !ok )
|
|
{
|
|
if ( !error.isEmpty() )
|
|
{
|
|
// TODO raise
|
|
}
|
|
}
|
|
|
|
if ( originalText == newText )
|
|
return;
|
|
|
|
// try to preserve the cursor position and scroll position
|
|
const int oldScrollValue = verticalScrollBar()->value();
|
|
const int linearPosition = findMinimalDistanceIndex( newText, textBeforeCursor );
|
|
|
|
beginUndoAction();
|
|
selectAll();
|
|
removeSelectedText();
|
|
insert( newText );
|
|
lineIndexFromPosition( linearPosition, &line, &index );
|
|
setCursorPosition( line, index );
|
|
verticalScrollBar()->setValue( oldScrollValue );
|
|
endUndoAction();
|
|
}
|
|
|
|
QStringList QgsCodeEditor::history() const
|
|
{
|
|
return mHistory;
|
|
}
|
|
|
|
void QgsCodeEditor::runCommand( const QString &command )
|
|
{
|
|
updateHistory( { command } );
|
|
|
|
if ( mInterpreter )
|
|
mInterpreter->exec( command );
|
|
|
|
clear();
|
|
moveCursorToEnd();
|
|
}
|
|
|
|
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
|
|
if ( hasSelectedText() )
|
|
{
|
|
replaceSelectedText( text );
|
|
}
|
|
else
|
|
{
|
|
int line, index;
|
|
getCursorPosition( &line, &index );
|
|
insertAt( text, line, index );
|
|
setCursorPosition( line, index + text.length() );
|
|
}
|
|
}
|
|
|
|
QColor QgsCodeEditor::defaultColor( QgsCodeEditorColorScheme::ColorRole role, const QString &theme )
|
|
{
|
|
if ( theme.isEmpty() && QgsApplication::themeName() == QLatin1String( "default" ) )
|
|
{
|
|
// if using default theme, take certain colors from the palette
|
|
const QPalette pal = qApp->palette();
|
|
|
|
switch ( role )
|
|
{
|
|
case QgsCodeEditorColorScheme::ColorRole::SelectionBackground:
|
|
return pal.color( QPalette::Highlight );
|
|
case QgsCodeEditorColorScheme::ColorRole::SelectionForeground:
|
|
return pal.color( QPalette::HighlightedText );
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if ( theme.isEmpty() )
|
|
{
|
|
// non default theme (e.g. Blend of Gray). Take colors from theme ini file...
|
|
const QSettings ini( QgsApplication::uiThemes().value( QgsApplication::themeName() ) + "/qscintilla.ini", QSettings::IniFormat );
|
|
|
|
static const QMap< QgsCodeEditorColorScheme::ColorRole, QString > sColorRoleToIniKey
|
|
{
|
|
{QgsCodeEditorColorScheme::ColorRole::Default, QStringLiteral( "python/defaultFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Keyword, QStringLiteral( "python/keywordFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Class, QStringLiteral( "python/classFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Method, QStringLiteral( "python/methodFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Decoration, QStringLiteral( "python/decoratorFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Number, QStringLiteral( "python/numberFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Comment, QStringLiteral( "python/commentFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::CommentLine, QStringLiteral( "sql/commentLineFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::CommentBlock, QStringLiteral( "python/commentBlockFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Background, QStringLiteral( "python/paperBackgroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Cursor, QStringLiteral( "cursorColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::CaretLine, QStringLiteral( "caretLineColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Operator, QStringLiteral( "sql/operatorFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::QuotedOperator, QStringLiteral( "sql/QuotedOperatorFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Identifier, QStringLiteral( "sql/identifierFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::QuotedIdentifier, QStringLiteral( "sql/QuotedIdentifierFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Tag, QStringLiteral( "html/tagFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::UnknownTag, QStringLiteral( "html/unknownTagFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::SingleQuote, QStringLiteral( "sql/singleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::DoubleQuote, QStringLiteral( "sql/doubleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::TripleSingleQuote, QStringLiteral( "python/tripleSingleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::TripleDoubleQuote, QStringLiteral( "python/tripleDoubleQuoteFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MarginBackground, QStringLiteral( "marginBackgroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MarginForeground, QStringLiteral( "marginForegroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::SelectionBackground, QStringLiteral( "selectionBackgroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::SelectionForeground, QStringLiteral( "selectionForegroundColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MatchedBraceBackground, QStringLiteral( "matchedBraceBackground" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::MatchedBraceForeground, QStringLiteral( "matchedBraceColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Edge, QStringLiteral( "edgeColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Fold, QStringLiteral( "foldColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::Error, QStringLiteral( "stderrFontColor" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::ErrorBackground, QStringLiteral( "stderrBackground" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::FoldIconForeground, QStringLiteral( "foldIconForeground" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::FoldIconHalo, QStringLiteral( "foldIconHalo" ) },
|
|
{QgsCodeEditorColorScheme::ColorRole::IndentationGuide, QStringLiteral( "indentationGuide" ) },
|
|
};
|
|
|
|
const QgsCodeEditorColorScheme defaultScheme = QgsGui::codeEditorColorSchemeRegistry()->scheme( QStringLiteral( "default" ) );
|
|
return QgsSymbolLayerUtils::decodeColor( ini.value( sColorRoleToIniKey.value( role ), defaultScheme.color( role ).name() ).toString() );
|
|
}
|
|
|
|
const QgsCodeEditorColorScheme scheme = QgsGui::codeEditorColorSchemeRegistry()->scheme( theme.isEmpty() ? QStringLiteral( "default" ) : theme );
|
|
return scheme.color( role );
|
|
}
|
|
|
|
QColor QgsCodeEditor::color( QgsCodeEditorColorScheme::ColorRole role )
|
|
{
|
|
const QgsSettings settings;
|
|
if ( !settings.value( QStringLiteral( "codeEditor/overrideColors" ), false, QgsSettings::Gui ).toBool() )
|
|
{
|
|
const QString theme = settings.value( QStringLiteral( "codeEditor/colorScheme" ), QString(), QgsSettings::Gui ).toString();
|
|
return defaultColor( role, theme );
|
|
}
|
|
else
|
|
{
|
|
const QString color = settings.value( QStringLiteral( "codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), QString(), QgsSettings::Gui ).toString();
|
|
return color.isEmpty() ? defaultColor( role ) : QgsSymbolLayerUtils::decodeColor( color );
|
|
}
|
|
}
|
|
|
|
void QgsCodeEditor::setColor( QgsCodeEditorColorScheme::ColorRole role, const QColor &color )
|
|
{
|
|
QgsSettings settings;
|
|
if ( color.isValid() )
|
|
{
|
|
settings.setValue( QStringLiteral( "codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), color.name(), QgsSettings::Gui );
|
|
}
|
|
else
|
|
{
|
|
settings.remove( QStringLiteral( "codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), QgsSettings::Gui );
|
|
}
|
|
}
|
|
|
|
// Settings for font and fontsize
|
|
bool QgsCodeEditor::isFixedPitch( const QFont &font )
|
|
{
|
|
return font.fixedPitch();
|
|
}
|
|
|
|
QFont QgsCodeEditor::getMonospaceFont()
|
|
{
|
|
QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
|
|
|
|
const QgsSettings settings;
|
|
if ( !settings.value( QStringLiteral( "codeEditor/fontfamily" ), QString(), QgsSettings::Gui ).toString().isEmpty() )
|
|
font.setFamily( settings.value( QStringLiteral( "codeEditor/fontfamily" ), QString(), QgsSettings::Gui ).toString() );
|
|
|
|
const int fontSize = settings.value( QStringLiteral( "codeEditor/fontsize" ), 0, QgsSettings::Gui ).toInt();
|
|
|
|
#ifdef Q_OS_MAC
|
|
if ( fontSize > 0 )
|
|
font.setPointSize( fontSize );
|
|
else
|
|
{
|
|
// The font size gotten from getMonospaceFont() is too small on Mac
|
|
font.setPointSize( QLabel().font().pointSize() );
|
|
}
|
|
#else
|
|
if ( fontSize > 0 )
|
|
font.setPointSize( fontSize );
|
|
else
|
|
{
|
|
const int fontSize = settings.value( QStringLiteral( "qgis/stylesheet/fontPointSize" ), 10 ).toInt();
|
|
font.setPointSize( fontSize );
|
|
}
|
|
#endif
|
|
font.setBold( false );
|
|
|
|
return font;
|
|
}
|
|
|
|
void QgsCodeEditor::setCustomAppearance( const QString &scheme, const QMap<QgsCodeEditorColorScheme::ColorRole, QColor> &customColors, const QString &fontFamily, int fontSize )
|
|
{
|
|
mUseDefaultSettings = false;
|
|
mOverrideColors = !customColors.isEmpty();
|
|
mColorScheme = scheme;
|
|
mCustomColors = customColors;
|
|
mFontFamily = fontFamily;
|
|
mFontSize = fontSize;
|
|
|
|
setSciWidget();
|
|
initializeLexer();
|
|
}
|
|
|
|
void QgsCodeEditor::addWarning( const int lineNumber, const QString &warning )
|
|
{
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), "000" );
|
|
markerAdd( lineNumber, MARKER_NUMBER );
|
|
QFont font = lexerFont();
|
|
font.setItalic( true );
|
|
const QsciStyle styleAnn = QsciStyle( -1, QStringLiteral( "Annotation" ),
|
|
lexerColor( QgsCodeEditorColorScheme::ColorRole::Error ),
|
|
lexerColor( QgsCodeEditorColorScheme::ColorRole::ErrorBackground ),
|
|
font,
|
|
true );
|
|
annotate( lineNumber, warning, styleAnn );
|
|
mWarningLines.push_back( lineNumber );
|
|
}
|
|
|
|
void QgsCodeEditor::clearWarnings()
|
|
{
|
|
for ( const int line : mWarningLines )
|
|
{
|
|
markerDelete( line );
|
|
clearAnnotations( line );
|
|
}
|
|
setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
|
|
mWarningLines.clear();
|
|
}
|
|
|
|
bool QgsCodeEditor::isCursorOnLastLine() const
|
|
{
|
|
int line = 0;
|
|
int index = 0;
|
|
getCursorPosition( &line, &index );
|
|
return line == lines() - 1;
|
|
}
|
|
|
|
void QgsCodeEditor::setHistoryFilePath( const QString &path )
|
|
{
|
|
mHistoryFilePath = path;
|
|
readHistoryFile();
|
|
}
|
|
|
|
void QgsCodeEditor::moveCursorToStart()
|
|
{
|
|
setCursorPosition( 0, 0 );
|
|
ensureCursorVisible();
|
|
ensureLineVisible( 0 );
|
|
|
|
if ( mMode == QgsCodeEditor::Mode::CommandInput )
|
|
updatePrompt();
|
|
}
|
|
|
|
void QgsCodeEditor::moveCursorToEnd()
|
|
{
|
|
const int endLine = lines() - 1;
|
|
const int endLineLength = lineLength( endLine );
|
|
setCursorPosition( endLine, endLineLength );
|
|
ensureCursorVisible();
|
|
ensureLineVisible( endLine );
|
|
|
|
if ( mMode == QgsCodeEditor::Mode::CommandInput )
|
|
updatePrompt();
|
|
}
|
|
|
|
QgsCodeInterpreter::~QgsCodeInterpreter() = default;
|
|
|
|
int QgsCodeInterpreter::exec( const QString &command )
|
|
{
|
|
mState = execCommandImpl( command );
|
|
return mState;
|
|
}
|