/* * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.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 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef EDITOR_H #define EDITOR_H #include #include #include #include "qsynedit/SynEdit.h" #include "colorscheme.h" #include "common.h" #include "parser/cppparser.h" #include "widgets/codecompletionpopup.h" #include "widgets/headercompletionpopup.h" #define USER_CODE_IN_INSERT_POS "%INSERT%" #define USER_CODE_IN_REPL_POS_BEGIN "%REPL_BEGIN%" #define USER_CODE_IN_REPL_POS_END "%REPL_END%" struct TabStop { int x; int endX; int y; }; using PTabStop = std::shared_ptr; class SaveException: public std::exception { public: explicit SaveException(const QString& reason); explicit SaveException(const QString&& reason); // exception interface const QString& reason() const noexcept; public: const char *what() const noexcept override; private: QString mReason; QByteArray mReasonBuffer; }; class Editor : public QSynedit::SynEdit { Q_OBJECT public: enum class LastSymbolType { Identifier, ScopeResolutionOperator, //'::' ObjectMemberOperator, //'.' PointerMemberOperator, //'->' PointerToMemberOfObjectOperator, //'.*' PointerToMemberOfPointerOperator, //'->*' MatchingBracket, BracketMatched, MatchingParenthesis, ParenthesisMatched, TildeSign, // '~' AsteriskSign, // '*' AmpersandSign, // '&' MatchingAngleQuotation, AngleQuotationMatched, None }; enum MarginNumber { LineNumberMargin = 0, MarkerMargin = 1, FoldMargin = 2, }; enum MarkerNumber { BreakpointMarker, ErrorMarker, WarningMarker }; enum class QuoteStatus { NotQuote, SingleQuote, SingleQuoteEscape, DoubleQuote, DoubleQuoteEscape, RawString, RawStringNoEscape }; enum class WordPurpose { wpCompletion, // walk backwards over words, array, functions, parents, no forward movement wpEvaluation, // walk backwards over words, array, functions, parents, forwards over words, array wpHeaderCompletion, // walk backwards over path wpHeaderCompletionStart, // walk backwards over path, including start '<' or '"' wpDirective, // preprocessor wpJavadoc, //javadoc wpInformation // walk backwards over words, array, functions, parents, forwards over words }; enum class TipType { Preprocessor, // cursor hovers above preprocessor line Identifier, // cursor hovers above identifier Selection, // cursor hovers above selection None, // mouseover not allowed Error //Cursor hovers above error line/item; }; struct SyntaxIssue { int col; int endCol; int startChar; int endChar; CompileIssueType issueType; QString token; QString hint; }; using PSyntaxIssue = std::shared_ptr; using SyntaxIssueList = QVector; using PSyntaxIssueList = std::shared_ptr; explicit Editor(QWidget *parent); explicit Editor(QWidget *parent, const QString& filename, const QByteArray& encoding, bool inProject, bool isNew,QTabWidget* parentPageControl); ~Editor(); //tell the compiler to prohibit copy/moving editor objects ( we should only use pointers to the editor object) Editor(const Editor&) = delete; Editor(const Editor&&) = delete; Editor& operator=(const Editor&) = delete; Editor& operator=(const Editor&&) = delete; const QByteArray& encodingOption() const noexcept; void setEncodingOption(const QByteArray& encoding) noexcept; const QByteArray& fileEncoding() const noexcept; void convertToEncoding(const QByteArray& encoding); const QString& filename() const noexcept; bool inProject() const noexcept; bool isNew() const noexcept; void loadFile(QString filename = ""); void saveFile(QString filename); bool save(bool force=false, bool reparse=true); bool saveAs(const QString& name="", bool fromProject = false); void activate(); QTabWidget* pageControl() noexcept; void setPageControl(QTabWidget* newPageControl); void updateCaption(const QString& newCaption=QString()); void applySettings(); void applyColorScheme(const QString& schemeName); void copyToClipboard() override; void cutToClipboard() override; void copyAsHTML(); void setCaretPosition(int line,int aChar); void setCaretPositionAndActivate(int line,int aChar); void addSyntaxIssues(int line, int startChar, int endChar, CompileIssueType errorType, const QString& hint); void clearSyntaxIssues(); void gotoNextSyntaxIssue(); void gotoPrevSyntaxIssue(); bool hasPrevSyntaxIssue() const; bool hasNextSyntaxIssue() const; PSyntaxIssueList getSyntaxIssuesAtLine(int line); PSyntaxIssue getSyntaxIssueAtPosition(const QSynedit::BufferCoord& pos); int gutterClickedLine() const; void toggleBreakpoint(int line); void clearBreakpoints(); bool hasBreakpoint(int line); void addBookmark(int line,const QString& description); void removeBookmark(int line); bool hasBookmark(int line); void clearBookmarks(); void removeBreakpointFocus(); void modifyBreakpointProperty(int line); void setActiveBreakpointFocus(int Line, bool setFocus=true); QString getPreviousWordAtPositionForSuggestion(const QSynedit::BufferCoord& p); void reformat(); void checkSyntaxInBack(); void gotoDeclaration(const QSynedit::BufferCoord& pos); void gotoDefinition(const QSynedit::BufferCoord& pos); void reparse(); void reparseTodo(); void insertString(const QString& value, bool moveCursor); void insertCodeSnippet(const QString& code); void print(); void exportAsRTF(const QString& rtfFilename); void exportAsHTML(const QString& htmlFilename); void resetBreakpoints(); bool notParsed(); void insertLine(); void deleteWord(); void deleteToWordStart(); void deleteToWordEnd(); void deleteLine(); void duplicateLine(); void deleteToEOL(); void deleteToBOL(); QStringList getOwnerExpressionAndMemberAtPositionForCompletion( const QSynedit::BufferCoord& pos, QString& memberOperator, QStringList& memberExpression); QString getWordForCompletionSearch(const QSynedit::BufferCoord& pos,bool permitTilde); QStringList getExpressionAtPosition( const QSynedit::BufferCoord& pos); const PCppParser &parser(); void tab() override; signals: void renamed(const QString& oldName, const QString& newName, bool firstSave); void fileSaved(const QString& filename, bool inProject); private slots: void onStatusChanged(QSynedit::SynStatusChanges changes); void onGutterClicked(Qt::MouseButton button, int x, int y, int line); void onTipEvalValueReady(const QString& value); void onLinesDeleted(int first,int count); void onLinesInserted(int first,int count); void onFunctionTipsTimer(); private: bool isBraceChar(QChar ch); void resetBookmarks(); QChar getCurrentChar(); bool handleSymbolCompletion(QChar key); bool handleParentheseCompletion(); bool handleParentheseSkip(); bool handleBracketCompletion(); bool handleBracketSkip(); bool handleMultilineCommentCompletion(); bool handleBraceCompletion(); bool handleBraceSkip(); bool handleSingleQuoteCompletion(); bool handleDoubleQuoteCompletion(); bool handleGlobalIncludeCompletion(); bool handleGlobalIncludeSkip(); bool handleCodeCompletion(QChar key); void initParser(); void undoSymbolCompletion(int pos); QuoteStatus getQuoteStatus(); void showCompletion(const QString& preWord, bool autoComplete); void showHeaderCompletion(bool autoComplete, bool forceShow=false); bool testInFunc(int x,int y); void completionInsert(bool appendFunc=false); void headerCompletionInsert(); bool onCompletionKeyPressed(QKeyEvent* event); bool onHeaderCompletionKeyPressed(QKeyEvent* event); bool onCompletionInputMethod(QInputMethodEvent *event); TipType getTipType(QPoint point, QSynedit::BufferCoord& pos); void cancelHint(); QString getFileHint(const QString& s); QString getParserHint(const QStringList& expression,const QString& s, int line); void showDebugHint(const QString& s,int line); QString getErrorHint(const PSyntaxIssue& issue); QString getHintForFunction(const PStatement& statement, const QString& filename, int line); void updateFunctionTip(bool showTip); void clearUserCodeInTabStops(); void popUserCodeInTabStops(); void onExportedFormatToken(QSynedit::PSynHighlighter syntaxHighlighter, int Line, int column, const QString& token, QSynedit::PSynHighlighterAttribute &attr); void onScrollBarValueChanged(); private: QByteArray mEncodingOption; // the encoding type set by the user QByteArray mFileEncoding; // the real encoding of the file (auto detected) QString mFilename; QTabWidget* mParentPageControl; bool mInProject; bool mIsNew; QMap mSyntaxIssues; QColor mSyntaxErrorColor; QColor mSyntaxWarningColor; QColor mActiveBreakpointForegroundColor; QColor mActiveBreakpointBackgroundColor; QColor mBreakpointForegroundColor; QColor mBreakpointBackgroundColor; QColor mCurrentHighlighWordForeground; QColor mCurrentHighlighWordBackground; int mSyntaxErrorLine; int mLineCount; int mGutterClickedLine; QSet mBreakpointLines; QSet mBookmarkLines; int mActiveBreakpointLine; PCppParser mParser; std::shared_ptr mCompletionPopup; std::shared_ptr mHeaderCompletionPopup; int mLastIdCharPressed; bool mUseCppSyntax; QString mCurrentWord; QString mCurrentDebugTipWord; TipType mCurrentTipType; QString mOldHighlightedWord; QString mCurrentHighlightedWord; QDateTime mHideTime; bool mSaving; bool mCurrentLineModified; int mXOffsetSince; int mTabStopBegin; int mTabStopEnd; int mTabStopY; bool mCanAutoSave; QString mLineBeforeTabStop; QString mLineAfterTabStop; QList mUserCodeInTabStops; QSynedit::BufferCoord mHighlightCharPos1; QSynedit::BufferCoord mHighlightCharPos2; std::shared_ptr > > mStatementColors; QTimer mFunctionTipTimer; int mHoverModifiedLine; // QWidget interface protected: void wheelEvent(QWheelEvent *event) override; void focusInEvent(QFocusEvent *event) override; void focusOutEvent(QFocusEvent *event) override; void keyPressEvent(QKeyEvent *event) override; // SynEdit interface protected: void onGutterPaint(QPainter &painter, int aLine, int X, int Y) override; void onGetEditingAreas(int Line, QSynedit::SynEditingAreaList &areaList) override; // SynEdit interface protected: bool onGetSpecialLineColors(int Line, QColor &foreground, QColor &backgroundColor) override; // SynEdit interface protected: void onPreparePaintHighlightToken(int line, int aChar, const QString &token, QSynedit::PSynHighlighterAttribute attr, QSynedit::SynFontStyles &style, QColor &foreground, QColor &background) override; // QObject interface public: bool event(QEvent *event) override; // QWidget interface void setInProject(bool newInProject); bool useCppSyntax() const; void setUseCppSyntax(bool newUseCppSyntax); const std::shared_ptr > > &statementColors() const; void setStatementColors(const std::shared_ptr > > &newStatementColors); const QDateTime &hideTime() const; void setHideTime(const QDateTime &newHideTime); bool canAutoSave() const; void setCanAutoSave(bool newCanAutoSave); protected: void mouseReleaseEvent(QMouseEvent *event) override; void inputMethodEvent(QInputMethodEvent *) override; void closeEvent(QCloseEvent *event) override; // QWidget interface protected: void showEvent(QShowEvent *event) override; void hideEvent(QHideEvent *event) override; // QWidget interface protected: void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; }; QString getWordAtPosition(QSynedit::SynEdit* editor, const QSynedit::BufferCoord& p, QSynedit::BufferCoord& pWordBegin, QSynedit::BufferCoord& pWordEnd, Editor::WordPurpose purpose); #endif // EDITOR_H