30#include <QFontDatabase>
33#include <Qsci/qscistyle.h>
39QMap< QgsCodeEditorColorScheme::ColorRole, QString > QgsCodeEditor::sColorRoleToSettingsKey
80 : QsciScintilla( parent )
81 , mWidgetTitle( title )
86 if ( !parent && mWidgetTitle.isEmpty() )
88 setWindowTitle( QStringLiteral(
"Text Editor" ) );
92 setWindowTitle( mWidgetTitle );
98 mSoftHistory.append( QString() );
101 setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
103 SendScintilla( SCI_SETADDITIONALSELECTIONTYPING, 1 );
104 SendScintilla( SCI_SETMULTIPASTE, 1 );
105 SendScintilla( SCI_SETVIRTUALSPACEOPTIONS, SCVS_RECTANGULARSELECTION );
110 setAnnotationDisplay( QsciScintilla::AnnotationBoxed );
126 SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
128 setWrapMode( QsciScintilla::WrapCharacter );
135 SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
137 setWrapMode( QsciScintilla::WrapCharacter );
138 SendScintilla( QsciScintilla::SCI_EMPTYUNDOBUFFER );
143#if QSCINTILLA_VERSION < 0x020d03
144 installEventFilter(
this );
151#if QSCINTILLA_VERSION >= 0x020800 && QSCINTILLA_VERSION < 0x020900
152 if ( event->reason() != Qt::ActiveWindowFocusReason )
166 QFocusEvent newFocusEvent( QEvent::FocusOut, Qt::ActiveWindowFocusReason );
167 QsciScintilla::focusOutEvent( &newFocusEvent );
172 QsciScintilla::focusOutEvent( event );
181 if ( isListActive() )
183 QsciScintilla::keyPressEvent( event );
187 if ( event->key() == Qt::Key_Escape )
190 QWidget::keyPressEvent( event );
196 switch ( event->key() )
219 const bool ctrlModifier =
event->modifiers() & Qt::ControlModifier;
220 const bool altModifier =
event->modifiers() & Qt::AltModifier;
224 if ( !isReadOnly() && canReformat && ctrlModifier && altModifier && event->key() == Qt::Key_F )
233 if ( !isReadOnly() && canToggle && ctrlModifier && event->key() == Qt::Key_Colon )
240 QsciScintilla::keyPressEvent( event );
250 QMenu *menu = createStandardContextMenu();
251 menu->setAttribute( Qt::WA_DeleteOnClose );
256 menu->addSeparator();
261 QAction *reformatAction =
new QAction( tr(
"Reformat Code" ), menu );
262 reformatAction->setShortcut( QStringLiteral(
"Ctrl+Alt+F" ) );
264 reformatAction->setEnabled( !isReadOnly() );
266 menu->addAction( reformatAction );
271 QAction *syntaxCheckAction =
new QAction( tr(
"Check Syntax" ), menu );
274 menu->addAction( syntaxCheckAction );
279 QAction *toggleCommentAction =
new QAction( tr(
"Toggle Comment" ), menu );
280 toggleCommentAction->setShortcut( QStringLiteral(
"Ctrl+:" ) );
282 toggleCommentAction->setEnabled( !isReadOnly() );
284 menu->addAction( toggleCommentAction );
289 menu->exec( mapToGlobal( event->pos() ) );
295 QMenu *menu =
new QMenu(
this );
296 QMenu *historySubMenu =
new QMenu( tr(
"Command History" ), menu );
302 menu->addMenu( historySubMenu );
303 menu->addSeparator();
305 QAction *copyAction = menu->addAction(
QgsApplication::getThemeIcon(
"mActionEditCopy.svg" ), tr(
"Copy" ),
this, &QgsCodeEditor::copy, QKeySequence::Copy );
306 QAction *pasteAction = menu->addAction(
QgsApplication::getThemeIcon(
"mActionEditPaste.svg" ), tr(
"Paste" ),
this, &QgsCodeEditor::paste, QKeySequence::Paste );
307 copyAction->setEnabled( hasSelectedText() );
308 pasteAction->setEnabled( !QApplication::clipboard()->text().isEmpty() );
312 menu->exec( mapToGlobal( event->pos() ) );
317 QsciScintilla::contextMenuEvent( event );
324#if QSCINTILLA_VERSION < 0x020d03
325 if ( watched ==
this && event->type() == QEvent::InputMethod )
334 return QsciScintilla::eventFilter( watched, event );
344 if ( mUseDefaultSettings )
345 return color( role );
347 if ( !mOverrideColors )
353 const QColor
color = mCustomColors.value( role );
360 if ( mUseDefaultSettings )
363 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
366 if ( !mFontFamily.isEmpty() )
371 font.setPointSize( mFontSize );
375 font.setPointSize( QLabel().font().pointSize() );
379 font.setPointSize( mFontSize );
382 const int fontSize = settings.
value( QStringLiteral(
"qgis/stylesheet/fontPointSize" ), 10 ).toInt();
383 font.setPointSize( fontSize );
386 font.setBold(
false );
407 setCaretLineVisible(
false );
410 setMarginLineNumbers( 1,
true );
411 setMarginWidth( 1,
"00000" );
412 setMarginType( 1, QsciScintilla::MarginType::TextMarginRightJustified );
414 setEdgeMode( QsciScintilla::EdgeNone );
418void QgsCodeEditor::setSciWidget()
424 setCaretLineVisible(
true );
430 setBraceMatching( QsciScintilla::SloppyBraceMatch );
437 setFolding( QsciScintilla::NoFoldStyle );
449 setFoldMarginColors( foldColor, foldColor );
451 setAutoIndent(
true );
452 setIndentationWidth( 4 );
453 setTabIndents(
true );
454 setBackspaceUnindents(
true );
457 setAutoCompletionThreshold( 2 );
458 setAutoCompletionSource( QsciScintilla::AcsAPIs );
466 setWindowTitle( title );
476 return Qgis::ScriptLanguageCapabilities();
486 return tr(
"Expression" );
490 return tr(
"JavaScript" );
494 return tr(
"Python" );
500 return tr(
"Batch" );
515 marginFont.setPointSize( 10 );
516 setMarginLineNumbers( 0,
true );
517 setMarginsFont( marginFont );
535 marginFont.setPointSize( 10 );
537 setMarginsFont( marginFont );
572void QgsCodeEditor::updateFolding()
579 setFolding( QsciScintilla::PlainFoldStyle );
583 setFolding( QsciScintilla::NoFoldStyle );
588bool QgsCodeEditor::readHistoryFile()
590 if ( mHistoryFilePath.isEmpty() || !QFile::exists( mHistoryFilePath ) )
593 QFile file( mHistoryFilePath );
594 if ( file.open( QIODevice::ReadOnly ) )
596 QTextStream stream( &file );
597#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
599 stream.setCodec(
"UTF-8" );
602 while ( !stream.atEnd() )
604 line = stream.readLine();
605 mHistory.append( line );
614void QgsCodeEditor::syncSoftHistory()
616 mSoftHistory = mHistory;
617 mSoftHistory.append( QString() );
618 mSoftHistoryIndex = mSoftHistory.length() - 1;
623 mSoftHistory[mSoftHistoryIndex] = text();
626void QgsCodeEditor::updateHistory(
const QStringList &commands,
bool skipSoftHistory )
628 if ( commands.size() > 1 )
630 mHistory.append( commands );
632 else if ( !commands.value( 0 ).isEmpty() )
634 const QString command = commands.value( 0 );
635 if ( mHistory.empty() || command != mHistory.constLast() )
636 mHistory.append( command );
639 if ( !skipSoftHistory )
660 QMessageBox::information(
this, title, message );
664 QMessageBox::warning(
this, title, message );
668 QMessageBox::critical(
this, title, message );
678 SendScintilla( QsciScintilla::SCI_MARGINSETTEXT,
static_cast< uintptr_t
>( 0 ), prompt.toUtf8().constData() );
689 mInterpreter = newInterpreter;
696 const int index = std::min( source.length(), target.length() );
702 int refDistanceMore = d0;
703 int refIndexMore = index;
704 if ( index < source.length() - 1 )
709 if ( newDistance <= refDistanceMore )
711 refDistanceMore = newDistance;
713 if ( refIndexMore == source.length() - 1 )
723 int refDistanceLess = d0;
724 int refIndexLess = index;
730 if ( newDistance <= refDistanceLess )
732 refDistanceLess = newDistance;
734 if ( refIndexLess == 0 )
744 if ( refDistanceMore < refDistanceLess )
756 const QString originalText = text();
759 if ( originalText == newText )
763 const int oldScrollValue = verticalScrollBar()->value();
768 removeSelectedText();
771 verticalScrollBar()->setValue( oldScrollValue );
794 updateHistory( { command } );
800 mInterpreter->
exec( command );
819 if ( !mHistoryFilePath.isEmpty() && QFile::exists( mHistoryFilePath ) )
821 QFile file( mHistoryFilePath );
822 file.open( QFile::WriteOnly | QFile::Truncate );
830 if ( mHistoryFilePath.isEmpty() )
833 QFile f( mHistoryFilePath );
834 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
839 QTextStream ts( &f );
840#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
841 ts.setCodec(
"UTF-8" );
843 for (
const QString &command : std::as_const( mHistory ) )
845 ts << command +
'\n';
852 if ( mSoftHistoryIndex < mSoftHistory.length() - 1 && !mSoftHistory.isEmpty() )
854 mSoftHistoryIndex += 1;
855 setText( mSoftHistory[mSoftHistoryIndex] );
862 if ( mSoftHistoryIndex > 0 && !mSoftHistory.empty() )
864 mSoftHistoryIndex -= 1;
865 setText( mSoftHistory[mSoftHistoryIndex] );
873 dialog->setAttribute( Qt::WA_DeleteOnClose );
876 dialog->activateWindow();
882 mHistory.removeAt( index );
883 mSoftHistory.removeAt( index );
884 if ( index < mSoftHistoryIndex )
886 mSoftHistoryIndex -= 1;
887 if ( mSoftHistoryIndex < 0 )
888 mSoftHistoryIndex = mSoftHistory.length() - 1;
895 if ( hasSelectedText() )
897 replaceSelectedText( text );
902 getCursorPosition( &line, &index );
903 insertAt( text, line, index );
904 setCursorPosition( line, index + text.length() );
913 const QPalette
pal = qApp->palette();
918 return pal.color( QPalette::Highlight );
920 return pal.color( QPalette::HighlightedText );
925 else if ( theme.isEmpty() )
930 static const QMap< QgsCodeEditorColorScheme::ColorRole, QString > sColorRoleToIniKey
974 return scheme.
color( role );
980 if ( !settings.
value( QStringLiteral(
"codeEditor/overrideColors" ),
false,
QgsSettings::Gui ).toBool() )
982 const QString theme = settings.
value( QStringLiteral(
"codeEditor/colorScheme" ), QString(),
QgsSettings::Gui ).toString();
987 const QString
color = settings.
value( QStringLiteral(
"codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), QString(),
QgsSettings::Gui ).toString();
995 if (
color.isValid() )
1001 settings.
remove( QStringLiteral(
"codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ),
QgsSettings::Gui );
1008 return font.fixedPitch();
1013 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
1016 if ( !settings.
value( QStringLiteral(
"codeEditor/fontfamily" ), QString(),
QgsSettings::Gui ).toString().isEmpty() )
1019 const int fontSize = settings.
value( QStringLiteral(
"codeEditor/fontsize" ), 0,
QgsSettings::Gui ).toInt();
1023 font.setPointSize( fontSize );
1027 font.setPointSize( QLabel().font().pointSize() );
1031 font.setPointSize( fontSize );
1034 const int fontSize = settings.
value( QStringLiteral(
"qgis/stylesheet/fontPointSize" ), 10 ).toInt();
1035 font.setPointSize( fontSize );
1038 font.setBold(
false );
1045 mUseDefaultSettings =
false;
1046 mOverrideColors = !customColors.isEmpty();
1047 mColorScheme = scheme;
1048 mCustomColors = customColors;
1049 mFontFamily = fontFamily;
1050 mFontSize = fontSize;
1059 markerAdd( lineNumber, MARKER_NUMBER );
1061 font.setItalic(
true );
1062 const QsciStyle styleAnn = QsciStyle( -1, QStringLiteral(
"Annotation" ),
1067 annotate( lineNumber, warning, styleAnn );
1068 mWarningLines.push_back( lineNumber );
1073 for (
const int line : mWarningLines )
1075 markerDelete( line );
1076 clearAnnotations( line );
1079 mWarningLines.clear();
1086 getCursorPosition( &line, &index );
1087 return line == lines() - 1;
1092 mHistoryFilePath = path;
1098 setCursorPosition( 0, 0 );
1099 ensureCursorVisible();
1100 ensureLineVisible( 0 );
1108 const int endLine = lines() - 1;
1109 const int endLineLength = lineLength( endLine );
1110 setCursorPosition( endLine, endLineLength );
1111 ensureCursorVisible();
1112 ensureLineVisible( endLine );
1121 getCursorPosition( &line, &index );
1122 return positionFromLineIndex( line, index );
1128 lineIndexFromPosition( linearIndex, &line, &index );
1129 setCursorPosition( line, index );
1134 int startLine, startIndex, _;
1135 getSelection( &startLine, &startIndex, &_, &_ );
1136 if ( startLine == -1 )
1140 return positionFromLineIndex( startLine, startIndex );
1145 int endLine, endIndex, _;
1146 getSelection( &_, &_, &endLine, &endIndex );
1147 if ( endLine == -1 )
1151 return positionFromLineIndex( endLine, endIndex );
1156 int startLine, startIndex, endLine, endIndex;
1157 lineIndexFromPosition( start, &startLine, &startIndex );
1158 lineIndexFromPosition( end, &endLine, &endIndex );
1159 setSelection( startLine, startIndex, endLine, endIndex );
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.
void setLinearSelection(int start, int end)
Convenience function to set the selection using linear indexes.
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).
int selectionEnd() const
Convenience function to return the end of the selection as a linear index Contrary to the getSelectio...
virtual void initializeLexer()
Called when the dialect specific code lexer needs to be initialized (or reinitialized).
int linearPosition() const
Convenience function to return the cursor position as a linear index.
void setTitle(const QString &title)
Set the widget title.
void setLinearPosition(int position)
Convenience function to set the cursor position as a linear index.
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.
int selectionStart() const
Convenience function to return the start of the selection as a linear index Contrary to the getSelect...
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()
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
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)