29#include <QFontDatabase>
32#include <Qsci/qscistyle.h>
38QMap< QgsCodeEditorColorScheme::ColorRole, QString > QgsCodeEditor::sColorRoleToSettingsKey
79 : QsciScintilla( parent )
80 , mWidgetTitle( title )
85 if ( !parent && mWidgetTitle.isEmpty() )
87 setWindowTitle( QStringLiteral(
"Text Editor" ) );
91 setWindowTitle( mWidgetTitle );
97 mSoftHistory.append( QString() );
100 setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
102 SendScintilla( SCI_SETADDITIONALSELECTIONTYPING, 1 );
103 SendScintilla( SCI_SETMULTIPASTE, 1 );
104 SendScintilla( SCI_SETVIRTUALSPACEOPTIONS, SCVS_RECTANGULARSELECTION );
109 setAnnotationDisplay( QsciScintilla::AnnotationBoxed );
125 SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
127 setWrapMode( QsciScintilla::WrapCharacter );
134 SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
136 setWrapMode( QsciScintilla::WrapCharacter );
137 SendScintilla( QsciScintilla::SCI_EMPTYUNDOBUFFER );
142#if QSCINTILLA_VERSION < 0x020d03
143 installEventFilter(
this );
150#if QSCINTILLA_VERSION >= 0x020800 && QSCINTILLA_VERSION < 0x020900
151 if ( event->reason() != Qt::ActiveWindowFocusReason )
165 QFocusEvent newFocusEvent( QEvent::FocusOut, Qt::ActiveWindowFocusReason );
166 QsciScintilla::focusOutEvent( &newFocusEvent );
171 QsciScintilla::focusOutEvent( event );
180 if ( isListActive() )
182 QsciScintilla::keyPressEvent( event );
186 if ( event->key() == Qt::Key_Escape )
189 QWidget::keyPressEvent( event );
195 switch ( event->key() )
218 const bool ctrlModifier =
event->modifiers() & Qt::ControlModifier;
219 const bool altModifier =
event->modifiers() & Qt::AltModifier;
223 if ( !isReadOnly() && canReformat && ctrlModifier && altModifier && event->key() == Qt::Key_F )
232 if ( !isReadOnly() && canToggle && ctrlModifier && event->key() == Qt::Key_Colon )
239 QsciScintilla::keyPressEvent( event );
249 QMenu *menu = createStandardContextMenu();
250 menu->setAttribute( Qt::WA_DeleteOnClose );
255 menu->addSeparator();
260 QAction *reformatAction =
new QAction( tr(
"Reformat Code" ), menu );
261 reformatAction->setShortcut( QStringLiteral(
"Ctrl+Alt+F" ) );
263 reformatAction->setEnabled( !isReadOnly() );
265 menu->addAction( reformatAction );
270 QAction *syntaxCheckAction =
new QAction( tr(
"Check Syntax" ), menu );
273 menu->addAction( syntaxCheckAction );
278 QAction *toggleCommentAction =
new QAction( tr(
"Toggle Comment" ), menu );
279 toggleCommentAction->setShortcut( QStringLiteral(
"Ctrl+:" ) );
281 toggleCommentAction->setEnabled( !isReadOnly() );
283 menu->addAction( toggleCommentAction );
288 menu->exec( mapToGlobal( event->pos() ) );
294 QMenu *menu =
new QMenu(
this );
295 QMenu *historySubMenu =
new QMenu( tr(
"Command History" ), menu );
301 menu->addMenu( historySubMenu );
302 menu->addSeparator();
304 QAction *copyAction = menu->addAction(
QgsApplication::getThemeIcon(
"mActionEditCopy.svg" ), tr(
"Copy" ),
this, &QgsCodeEditor::copy, QKeySequence::Copy );
305 QAction *pasteAction = menu->addAction(
QgsApplication::getThemeIcon(
"mActionEditPaste.svg" ), tr(
"Paste" ),
this, &QgsCodeEditor::paste, QKeySequence::Paste );
306 copyAction->setEnabled( hasSelectedText() );
307 pasteAction->setEnabled( !QApplication::clipboard()->text().isEmpty() );
311 menu->exec( mapToGlobal( event->pos() ) );
316 QsciScintilla::contextMenuEvent( event );
323#if QSCINTILLA_VERSION < 0x020d03
324 if ( watched ==
this && event->type() == QEvent::InputMethod )
333 return QsciScintilla::eventFilter( watched, event );
343 if ( mUseDefaultSettings )
344 return color( role );
346 if ( !mOverrideColors )
352 const QColor
color = mCustomColors.value( role );
359 if ( mUseDefaultSettings )
362 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
365 if ( !mFontFamily.isEmpty() )
366 font.setFamily( mFontFamily );
370 font.setPointSize( mFontSize );
374 font.setPointSize( QLabel().font().pointSize() );
378 font.setPointSize( mFontSize );
381 const int fontSize = settings.
value( QStringLiteral(
"qgis/stylesheet/fontPointSize" ), 10 ).toInt();
382 font.setPointSize( fontSize );
385 font.setBold(
false );
406 setCaretLineVisible(
false );
409 setMarginLineNumbers( 1,
true );
410 setMarginWidth( 1,
"00000" );
411 setMarginType( 1, QsciScintilla::MarginType::TextMarginRightJustified );
413 setEdgeMode( QsciScintilla::EdgeNone );
417void QgsCodeEditor::setSciWidget()
423 setCaretLineVisible(
true );
429 setBraceMatching( QsciScintilla::SloppyBraceMatch );
436 setFolding( QsciScintilla::NoFoldStyle );
448 setFoldMarginColors( foldColor, foldColor );
450 setAutoIndent(
true );
451 setIndentationWidth( 4 );
452 setTabIndents(
true );
453 setBackspaceUnindents(
true );
456 setAutoCompletionThreshold( 2 );
457 setAutoCompletionSource( QsciScintilla::AcsAPIs );
465 setWindowTitle( title );
475 return Qgis::ScriptLanguageCapabilities();
485 return tr(
"Expression" );
489 return tr(
"JavaScript" );
493 return tr(
"Python" );
499 return tr(
"Batch" );
514 marginFont.setPointSize( 10 );
515 setMarginLineNumbers( 0,
true );
516 setMarginsFont( marginFont );
534 marginFont.setPointSize( 10 );
536 setMarginsFont( marginFont );
571void QgsCodeEditor::updateFolding()
578 setFolding( QsciScintilla::PlainFoldStyle );
582 setFolding( QsciScintilla::NoFoldStyle );
587bool QgsCodeEditor::readHistoryFile()
589 if ( mHistoryFilePath.isEmpty() || !QFile::exists( mHistoryFilePath ) )
592 QFile file( mHistoryFilePath );
593 if ( file.open( QIODevice::ReadOnly ) )
595 QTextStream stream( &file );
596#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
598 stream.setCodec(
"UTF-8" );
601 while ( !stream.atEnd() )
603 line = stream.readLine();
604 mHistory.append( line );
613void QgsCodeEditor::syncSoftHistory()
615 mSoftHistory = mHistory;
616 mSoftHistory.append( QString() );
617 mSoftHistoryIndex = mSoftHistory.length() - 1;
622 mSoftHistory[mSoftHistoryIndex] = text();
625void QgsCodeEditor::updateHistory(
const QStringList &commands,
bool skipSoftHistory )
627 if ( commands.size() > 1 )
629 mHistory.append( commands );
631 else if ( !commands.value( 0 ).isEmpty() )
633 const QString command = commands.value( 0 );
634 if ( mHistory.empty() || command != mHistory.constLast() )
635 mHistory.append( command );
638 if ( !skipSoftHistory )
659 QMessageBox::information(
this, title, message );
663 QMessageBox::warning(
this, title, message );
667 QMessageBox::critical(
this, title, message );
677 SendScintilla( QsciScintilla::SCI_MARGINSETTEXT,
static_cast< uintptr_t
>( 0 ), prompt.toUtf8().constData() );
688 mInterpreter = newInterpreter;
695 const int index = std::min( source.length(), target.length() );
701 int refDistanceMore = d0;
702 int refIndexMore = index;
703 if ( index < source.length() - 1 )
708 if ( newDistance <= refDistanceMore )
710 refDistanceMore = newDistance;
712 if ( refIndexMore == source.length() - 1 )
722 int refDistanceLess = d0;
723 int refIndexLess = index;
729 if ( newDistance <= refDistanceLess )
731 refDistanceLess = newDistance;
733 if ( refIndexLess == 0 )
743 if ( refDistanceMore < refDistanceLess )
756 getCursorPosition( &line, &index );
757 const QString textBeforeCursor = text( 0, positionFromLineIndex( line, index ) );
759 const QString originalText = text();
763 if ( originalText == newText )
767 const int oldScrollValue = verticalScrollBar()->value();
772 removeSelectedText();
774 lineIndexFromPosition( linearPosition, &line, &index );
775 setCursorPosition( line, index );
776 verticalScrollBar()->setValue( oldScrollValue );
799 updateHistory( { command } );
805 mInterpreter->
exec( command );
824 if ( !mHistoryFilePath.isEmpty() && QFile::exists( mHistoryFilePath ) )
826 QFile file( mHistoryFilePath );
827 file.open( QFile::WriteOnly | QFile::Truncate );
835 if ( mHistoryFilePath.isEmpty() )
838 QFile f( mHistoryFilePath );
839 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
844 QTextStream ts( &f );
845#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
846 ts.setCodec(
"UTF-8" );
848 for (
const QString &command : std::as_const( mHistory ) )
850 ts << command +
'\n';
857 if ( mSoftHistoryIndex < mSoftHistory.length() - 1 && !mSoftHistory.isEmpty() )
859 mSoftHistoryIndex += 1;
860 setText( mSoftHistory[mSoftHistoryIndex] );
867 if ( mSoftHistoryIndex > 0 && !mSoftHistory.empty() )
869 mSoftHistoryIndex -= 1;
870 setText( mSoftHistory[mSoftHistoryIndex] );
878 dialog->setAttribute( Qt::WA_DeleteOnClose );
881 dialog->activateWindow();
887 mHistory.removeAt( index );
888 mSoftHistory.removeAt( index );
889 if ( index < mSoftHistoryIndex )
891 mSoftHistoryIndex -= 1;
892 if ( mSoftHistoryIndex < 0 )
893 mSoftHistoryIndex = mSoftHistory.length() - 1;
900 if ( hasSelectedText() )
902 replaceSelectedText( text );
907 getCursorPosition( &line, &index );
908 insertAt( text, line, index );
909 setCursorPosition( line, index + text.length() );
918 const QPalette
pal = qApp->palette();
923 return pal.color( QPalette::Highlight );
925 return pal.color( QPalette::HighlightedText );
930 else if ( theme.isEmpty() )
935 static const QMap< QgsCodeEditorColorScheme::ColorRole, QString > sColorRoleToIniKey
979 return scheme.
color( role );
985 if ( !settings.
value( QStringLiteral(
"codeEditor/overrideColors" ),
false,
QgsSettings::Gui ).toBool() )
987 const QString theme = settings.
value( QStringLiteral(
"codeEditor/colorScheme" ), QString(),
QgsSettings::Gui ).toString();
992 const QString
color = settings.
value( QStringLiteral(
"codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), QString(),
QgsSettings::Gui ).toString();
1000 if (
color.isValid() )
1006 settings.
remove( QStringLiteral(
"codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ),
QgsSettings::Gui );
1013 return font.fixedPitch();
1018 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
1021 if ( !settings.
value( QStringLiteral(
"codeEditor/fontfamily" ), QString(),
QgsSettings::Gui ).toString().isEmpty() )
1022 font.setFamily( settings.
value( QStringLiteral(
"codeEditor/fontfamily" ), QString(),
QgsSettings::Gui ).toString() );
1024 const int fontSize = settings.
value( QStringLiteral(
"codeEditor/fontsize" ), 0,
QgsSettings::Gui ).toInt();
1028 font.setPointSize( fontSize );
1032 font.setPointSize( QLabel().font().pointSize() );
1036 font.setPointSize( fontSize );
1039 const int fontSize = settings.
value( QStringLiteral(
"qgis/stylesheet/fontPointSize" ), 10 ).toInt();
1040 font.setPointSize( fontSize );
1043 font.setBold(
false );
1050 mUseDefaultSettings =
false;
1051 mOverrideColors = !customColors.isEmpty();
1052 mColorScheme = scheme;
1053 mCustomColors = customColors;
1054 mFontFamily = fontFamily;
1055 mFontSize = fontSize;
1064 markerAdd( lineNumber, MARKER_NUMBER );
1066 font.setItalic(
true );
1067 const QsciStyle styleAnn = QsciStyle( -1, QStringLiteral(
"Annotation" ),
1072 annotate( lineNumber, warning, styleAnn );
1073 mWarningLines.push_back( lineNumber );
1078 for (
const int line : mWarningLines )
1080 markerDelete( line );
1081 clearAnnotations( line );
1084 mWarningLines.clear();
1091 getCursorPosition( &line, &index );
1092 return line == lines() - 1;
1097 mHistoryFilePath = path;
1103 setCursorPosition( 0, 0 );
1104 ensureCursorVisible();
1105 ensureLineVisible( 0 );
1113 const int endLine = lines() - 1;
1114 const int endLineLength = lineLength( endLine );
1115 setCursorPosition( endLine, endLineLength );
1116 ensureCursorVisible();
1117 ensureLineVisible( endLine );
MessageLevel
Level for messages This will be used both for message log and message bar in application.
@ Warning
Warning message.
@ Critical
Critical/error message.
@ Info
Information message.
@ Success
Used for reporting a successful operation.
@ CheckSyntax
Language supports syntax checking.
@ Reformat
Language supports automatic code reformatting.
@ ToggleComment
Language supports comment toggling.
ScriptLanguage
Scripting languages.
@ QgisExpression
QGIS expressions.
@ Batch
Windows batch files.
@ Unknown
Unknown/other language.
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QHash< QString, QString > uiThemes()
All themes found in ~/.qgis3/themes folder.
static QString themeName()
Set the active theme to the specified theme.
QgsCodeEditorColorScheme scheme(const QString &id) const
Returns the color scheme with matching id.
Defines a color scheme for use in QgsCodeEditor widgets.
@ TripleSingleQuote
Triple single quote color.
@ CommentBlock
Comment block color.
@ QuotedOperator
Quoted operator color.
@ Decoration
Decoration color.
@ Identifier
Identifier color.
@ DoubleQuote
Double quote color.
@ QuotedIdentifier
Quoted identifier color.
@ SelectionForeground
Selection foreground color.
@ CommentLine
Line comment color.
@ FoldIconForeground
Fold icon foreground color.
@ MarginForeground
Margin foreground color.
@ ErrorBackground
Error background color.
@ MatchedBraceBackground
Matched brace background color.
@ Default
Default text color.
@ CaretLine
Caret line color.
@ IndentationGuide
Indentation guide line.
@ Background
Background color.
@ SingleQuote
Single quote color.
@ MarginBackground
Margin background color.
@ SelectionBackground
Selection background color.
@ MatchedBraceForeground
Matched brace foreground color.
@ Operator
Operator color.
@ TripleDoubleQuote
Triple double quote color.
@ FoldIconHalo
Fold icon halo color.
QColor color(ColorRole role) const
Returns the color to use in the editor for the specified role.
A dialog for displaying and managing command history for a QgsCodeEditor widget.
bool eventFilter(QObject *watched, QEvent *event) override
void sessionHistoryCleared()
Emitted when the history of commands run in the current session is cleared.
void showHistory()
Shows the command history dialog.
void setCustomAppearance(const QString &scheme=QString(), const QMap< QgsCodeEditorColorScheme::ColorRole, QColor > &customColors=QMap< QgsCodeEditorColorScheme::ColorRole, QColor >(), const QString &fontFamily=QString(), int fontSize=0)
Sets a custom appearance for the widget, disconnecting it from using the standard appearance taken fr...
@ OutputDisplay
Read only mode for display of command outputs.
@ ScriptEditor
Standard mode, allows for display and edit of entire scripts.
@ CommandInput
Command input mode.
void reformatCode()
Applies code reformatting to the editor.
virtual void toggleComment()
Toggle comment for the selected text.
void contextMenuEvent(QContextMenuEvent *event) override
void clearPersistentHistory()
Clears the entire persistent history of commands run in the editor.
void removeHistoryCommand(int index)
Removes the command at the specified index from the history of the code editor.
static void setColor(QgsCodeEditorColorScheme::ColorRole role, const QColor &color)
Sets the color to use in the editor for the specified role.
void setHistoryFilePath(const QString &path)
Sets the file path to use for recording and retrieving previously executed commands.
QStringList history() const
Returns the list of commands previously executed in the editor.
void keyPressEvent(QKeyEvent *event) override
virtual void moveCursorToStart()
Moves the cursor to the start of the document and scrolls to ensure it is visible.
virtual void populateContextMenu(QMenu *menu)
Called when the context menu for the widget is about to be shown, after it has been fully populated w...
void persistentHistoryCleared()
Emitted when the persistent history of commands run in the editor is cleared.
void runCommand(const QString &command, bool skipHistory=false)
Runs a command in the editor.
void setFoldingVisible(bool folding)
Set whether the folding controls are visible in the editor.
virtual Qgis::ScriptLanguageCapabilities languageCapabilities() const
Returns the associated scripting language capabilities.
void setInterpreter(QgsCodeInterpreter *newInterpreter)
Sets an attached code interpreter for executing commands when the editor is in the QgsCodeEditor::Mod...
@ FoldingControls
Folding controls.
@ ErrorIndicators
Error indicators.
@ LineNumbers
Line numbers.
void runPostLexerConfigurationTasks()
Performs tasks which must be run after a lexer has been set for the widget.
virtual void showMessage(const QString &title, const QString &message, Qgis::MessageLevel level)
Shows a user facing message (eg a warning message).
virtual void initializeLexer()
Called when the dialect specific code lexer needs to be initialized (or reinitialized).
void setTitle(const QString &title)
Set the widget title.
QgsCodeEditor(QWidget *parent=nullptr, const QString &title=QString(), bool folding=false, bool margin=false, QgsCodeEditor::Flags flags=QgsCodeEditor::Flags(), QgsCodeEditor::Mode mode=QgsCodeEditor::Mode::ScriptEditor)
Flags controlling behavior of code editor.
void clearWarnings()
Clears all warning messages from the editor.
static QFont getMonospaceFont()
Returns the monospaced font to use for code editors.
void showNextCommand()
Shows the next command from the session in the editor.
void focusOutEvent(QFocusEvent *event) override
@ CodeFolding
Indicates that code folding should be enabled for the editor.
@ ImmediatelyUpdateHistory
Indicates that the history file should be immediately updated whenever a command is executed,...
bool isCursorOnLastLine() const
Returns true if the cursor is on the last line of the document.
static bool isFixedPitch(const QFont &font)
Returns true if a font is a fixed pitch font.
void updateSoftHistory()
Updates the soft history by storing the current editor text in the history.
void clearSessionHistory()
Clears the history of commands run in the current session.
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
bool writeHistoryFile()
Stores the commands executed in the editor to the persistent history file.
virtual void moveCursorToEnd()
Moves the cursor to the end of the document and scrolls to ensure it is visible.
static QString languageToString(Qgis::ScriptLanguage language)
Returns a user-friendly, translated name of the specified script language.
void setLineNumbersVisible(bool visible)
Sets whether line numbers should be visible in the editor.
virtual Qgis::ScriptLanguage language() const
Returns the associated scripting language.
QFont lexerFont() const
Returns the font to use in the lexer.
virtual QString reformatCodeString(const QString &string)
Applies code reformatting to a string and returns the result.
QgsCodeInterpreter * interpreter() const
Returns the attached code interpreter, or nullptr if not set.
bool lineNumbersVisible() const
Returns whether line numbers are visible in the editor.
QColor lexerColor(QgsCodeEditorColorScheme::ColorRole role) const
Returns the color to use in the lexer for the specified role.
bool foldingVisible()
Returns true if the folding controls are visible in the editor.
void showPreviousCommand()
Shows the previous command from the session in the editor.
Q_DECL_DEPRECATED void setMarginVisible(bool margin)
Set margin visible state.
void updatePrompt()
Triggers an update of the interactive prompt part of the editor.
static QColor defaultColor(QgsCodeEditorColorScheme::ColorRole role, const QString &theme=QString())
Returns the default color for the specified role.
void addWarning(int lineNumber, const QString &warning)
Adds a warning message and indicator to the specified a lineNumber.
virtual bool checkSyntax()
Applies syntax checking to the editor.
static QColor color(QgsCodeEditorColorScheme::ColorRole role)
Returns the color to use in the editor for the specified role.
An interface for code interpreters.
virtual int execCommandImpl(const QString &command)=0
Pure virtual method for executing commands in the interpreter.
virtual int currentState() const
Returns the current interpreter state.
virtual QString promptForState(int state) const =0
Returns the interactive prompt string to use for the interpreter, given a state.
int exec(const QString &command)
Executes a command in the interpreter.
virtual ~QgsCodeInterpreter()
void optionsChanged()
This signal is emitted whenever the application options have been changed.
static QgsGui * instance()
Returns a pointer to the singleton instance.
static QgsCodeEditorColorSchemeRegistry * codeEditorColorSchemeRegistry()
Returns the global code editor color scheme registry, used for registering the color schemes for QgsC...
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
static QColor decodeColor(const QString &str)
#define BUILTIN_UNREACHABLE
int findMinimalDistanceIndex(const QString &source, const QString &target)