| 1 | /* |
| 2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
| 3 | * |
| 4 | * This program is free software: you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License as published by |
| 6 | * the Free Software Foundation, either version 3 of the License, or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
| 16 | */ |
| 17 | #ifndef EDITOR_H |
| 18 | #define EDITOR_H |
| 19 | |
| 20 | #include <QObject> |
| 21 | #include <utils.h> |
| 22 | #include <QTabWidget> |
| 23 | #include "qsynedit/SynEdit.h" |
| 24 | #include "colorscheme.h" |
| 25 | #include "common.h" |
| 26 | #include "parser/cppparser.h" |
| 27 | #include "widgets/codecompletionpopup.h" |
| 28 | #include "widgets/headercompletionpopup.h" |
| 29 | |
| 30 | #define USER_CODE_IN_INSERT_POS "%INSERT%" |
| 31 | #define USER_CODE_IN_REPL_POS_BEGIN "%REPL_BEGIN%" |
| 32 | #define USER_CODE_IN_REPL_POS_END "%REPL_END%" |
| 33 | |
| 34 | struct TabStop { |
| 35 | int x; |
| 36 | int endX; |
| 37 | int y; |
| 38 | }; |
| 39 | |
| 40 | using PTabStop = std::shared_ptr<TabStop>; |
| 41 | |
| 42 | class SaveException: public std::exception { |
| 43 | |
| 44 | public: |
| 45 | explicit SaveException(const QString& reason); |
| 46 | explicit SaveException(const QString&& reason); |
| 47 | // exception interface |
| 48 | const QString& reason() const noexcept; |
| 49 | public: |
| 50 | const char *what() const noexcept override; |
| 51 | private: |
| 52 | QString mReason; |
| 53 | QByteArray mReasonBuffer; |
| 54 | }; |
| 55 | |
| 56 | class Editor : public QSynedit::SynEdit |
| 57 | { |
| 58 | Q_OBJECT |
| 59 | public: |
| 60 | enum class LastSymbolType { |
| 61 | Identifier, |
| 62 | ScopeResolutionOperator, //'::' |
| 63 | ObjectMemberOperator, //'.' |
| 64 | PointerMemberOperator, //'->' |
| 65 | PointerToMemberOfObjectOperator, //'.*' |
| 66 | PointerToMemberOfPointerOperator, //'->*' |
| 67 | MatchingBracket, |
| 68 | BracketMatched, |
| 69 | MatchingParenthesis, |
| 70 | ParenthesisMatched, |
| 71 | TildeSign, // '~' |
| 72 | AsteriskSign, // '*' |
| 73 | AmpersandSign, // '&' |
| 74 | MatchingAngleQuotation, |
| 75 | AngleQuotationMatched, |
| 76 | None |
| 77 | }; |
| 78 | |
| 79 | enum MarginNumber { |
| 80 | LineNumberMargin = 0, |
| 81 | MarkerMargin = 1, |
| 82 | FoldMargin = 2, |
| 83 | }; |
| 84 | |
| 85 | enum MarkerNumber { |
| 86 | BreakpointMarker, |
| 87 | ErrorMarker, |
| 88 | WarningMarker |
| 89 | }; |
| 90 | |
| 91 | enum class QuoteStatus { |
| 92 | NotQuote, |
| 93 | SingleQuote, |
| 94 | SingleQuoteEscape, |
| 95 | DoubleQuote, |
| 96 | DoubleQuoteEscape, |
| 97 | RawString, |
| 98 | RawStringNoEscape |
| 99 | }; |
| 100 | |
| 101 | enum class WordPurpose { |
| 102 | wpCompletion, // walk backwards over words, array, functions, parents, no forward movement |
| 103 | wpEvaluation, // walk backwards over words, array, functions, parents, forwards over words, array |
| 104 | , // walk backwards over path |
| 105 | , // walk backwards over path, including start '<' or '"' |
| 106 | wpDirective, // preprocessor |
| 107 | wpJavadoc, //javadoc |
| 108 | wpInformation // walk backwards over words, array, functions, parents, forwards over words |
| 109 | }; |
| 110 | |
| 111 | enum class TipType { |
| 112 | Preprocessor, // cursor hovers above preprocessor line |
| 113 | Identifier, // cursor hovers above identifier |
| 114 | Selection, // cursor hovers above selection |
| 115 | None, // mouseover not allowed |
| 116 | Error //Cursor hovers above error line/item; |
| 117 | }; |
| 118 | |
| 119 | struct SyntaxIssue { |
| 120 | int col; |
| 121 | int endCol; |
| 122 | int startChar; |
| 123 | int endChar; |
| 124 | CompileIssueType issueType; |
| 125 | QString token; |
| 126 | QString hint; |
| 127 | }; |
| 128 | |
| 129 | using PSyntaxIssue = std::shared_ptr<SyntaxIssue>; |
| 130 | using SyntaxIssueList = QVector<PSyntaxIssue>; |
| 131 | using PSyntaxIssueList = std::shared_ptr<SyntaxIssueList>; |
| 132 | |
| 133 | explicit Editor(QWidget *parent); |
| 134 | |
| 135 | explicit Editor(QWidget *parent, const QString& filename, |
| 136 | const QByteArray& encoding, |
| 137 | bool inProject, bool isNew,QTabWidget* parentPageControl); |
| 138 | |
| 139 | ~Editor(); |
| 140 | |
| 141 | //tell the compiler to prohibit copy/moving editor objects ( we should only use pointers to the editor object) |
| 142 | Editor(const Editor&) = delete; |
| 143 | Editor(const Editor&&) = delete; |
| 144 | Editor& operator=(const Editor&) = delete; |
| 145 | Editor& operator=(const Editor&&) = delete; |
| 146 | |
| 147 | const QByteArray& encodingOption() const noexcept; |
| 148 | void setEncodingOption(const QByteArray& encoding) noexcept; |
| 149 | const QByteArray& fileEncoding() const noexcept; |
| 150 | void convertToEncoding(const QByteArray& encoding); |
| 151 | const QString& filename() const noexcept; |
| 152 | |
| 153 | bool inProject() const noexcept; |
| 154 | bool isNew() const noexcept; |
| 155 | |
| 156 | void loadFile(QString filename = "" ); |
| 157 | void saveFile(QString filename); |
| 158 | bool save(bool force=false, bool reparse=true); |
| 159 | bool saveAs(const QString& name="" , bool fromProject = false); |
| 160 | void activate(); |
| 161 | |
| 162 | QTabWidget* pageControl() noexcept; |
| 163 | void setPageControl(QTabWidget* newPageControl); |
| 164 | |
| 165 | void updateCaption(const QString& newCaption=QString()); |
| 166 | void applySettings(); |
| 167 | void applyColorScheme(const QString& schemeName); |
| 168 | |
| 169 | void copyToClipboard() override; |
| 170 | void cutToClipboard() override; |
| 171 | void copyAsHTML(); |
| 172 | |
| 173 | void setCaretPosition(int line,int aChar); |
| 174 | void setCaretPositionAndActivate(int line,int aChar); |
| 175 | |
| 176 | void addSyntaxIssues(int line, int startChar, int endChar, CompileIssueType errorType, const QString& hint); |
| 177 | void clearSyntaxIssues(); |
| 178 | void gotoNextSyntaxIssue(); |
| 179 | void gotoPrevSyntaxIssue(); |
| 180 | bool hasPrevSyntaxIssue() const; |
| 181 | bool hasNextSyntaxIssue() const; |
| 182 | PSyntaxIssueList getSyntaxIssuesAtLine(int line); |
| 183 | PSyntaxIssue getSyntaxIssueAtPosition(const QSynedit::BufferCoord& pos); |
| 184 | int gutterClickedLine() const; |
| 185 | void toggleBreakpoint(int line); |
| 186 | void clearBreakpoints(); |
| 187 | bool hasBreakpoint(int line); |
| 188 | void addBookmark(int line,const QString& description); |
| 189 | void removeBookmark(int line); |
| 190 | bool hasBookmark(int line); |
| 191 | void clearBookmarks(); |
| 192 | void removeBreakpointFocus(); |
| 193 | void modifyBreakpointProperty(int line); |
| 194 | void setActiveBreakpointFocus(int Line, bool setFocus=true); |
| 195 | QString getPreviousWordAtPositionForSuggestion(const QSynedit::BufferCoord& p); |
| 196 | void reformat(); |
| 197 | void checkSyntaxInBack(); |
| 198 | void gotoDeclaration(const QSynedit::BufferCoord& pos); |
| 199 | void gotoDefinition(const QSynedit::BufferCoord& pos); |
| 200 | void reparse(); |
| 201 | void reparseTodo(); |
| 202 | void insertString(const QString& value, bool moveCursor); |
| 203 | void insertCodeSnippet(const QString& code); |
| 204 | void print(); |
| 205 | void exportAsRTF(const QString& rtfFilename); |
| 206 | void exportAsHTML(const QString& htmlFilename); |
| 207 | void resetBreakpoints(); |
| 208 | bool notParsed(); |
| 209 | void insertLine(); |
| 210 | void deleteWord(); |
| 211 | void deleteToWordStart(); |
| 212 | void deleteToWordEnd(); |
| 213 | void deleteLine(); |
| 214 | void duplicateLine(); |
| 215 | void deleteToEOL(); |
| 216 | void deleteToBOL(); |
| 217 | |
| 218 | QStringList getOwnerExpressionAndMemberAtPositionForCompletion( |
| 219 | const QSynedit::BufferCoord& pos, |
| 220 | QString& memberOperator, |
| 221 | QStringList& memberExpression); |
| 222 | QString getWordForCompletionSearch(const QSynedit::BufferCoord& pos,bool permitTilde); |
| 223 | QStringList getExpressionAtPosition( |
| 224 | const QSynedit::BufferCoord& pos); |
| 225 | |
| 226 | const PCppParser &parser(); |
| 227 | |
| 228 | void tab() override; |
| 229 | signals: |
| 230 | void renamed(const QString& oldName, const QString& newName, bool firstSave); |
| 231 | void fileSaved(const QString& filename, bool inProject); |
| 232 | private slots: |
| 233 | void onStatusChanged(QSynedit::StatusChanges changes); |
| 234 | void onGutterClicked(Qt::MouseButton button, int x, int y, int line); |
| 235 | void onTipEvalValueReady(const QString& value); |
| 236 | void onLinesDeleted(int first,int count); |
| 237 | void onLinesInserted(int first,int count); |
| 238 | void onFunctionTipsTimer(); |
| 239 | |
| 240 | private: |
| 241 | bool isBraceChar(QChar ch); |
| 242 | void resetBookmarks(); |
| 243 | QChar getCurrentChar(); |
| 244 | bool handleSymbolCompletion(QChar key); |
| 245 | bool handleParentheseCompletion(); |
| 246 | bool handleParentheseSkip(); |
| 247 | bool handleBracketCompletion(); |
| 248 | bool handleBracketSkip(); |
| 249 | bool handleMultilineCommentCompletion(); |
| 250 | bool handleBraceCompletion(); |
| 251 | bool handleBraceSkip(); |
| 252 | bool handleSingleQuoteCompletion(); |
| 253 | bool handleDoubleQuoteCompletion(); |
| 254 | bool handleGlobalIncludeCompletion(); |
| 255 | bool handleGlobalIncludeSkip(); |
| 256 | |
| 257 | bool handleCodeCompletion(QChar key); |
| 258 | void initParser(); |
| 259 | void undoSymbolCompletion(int pos); |
| 260 | QuoteStatus getQuoteStatus(); |
| 261 | |
| 262 | void showCompletion(const QString& preWord, bool autoComplete); |
| 263 | void (bool autoComplete, bool forceShow=false); |
| 264 | |
| 265 | bool testInFunc(int x,int y); |
| 266 | |
| 267 | void completionInsert(bool appendFunc=false); |
| 268 | |
| 269 | void (); |
| 270 | |
| 271 | bool onCompletionKeyPressed(QKeyEvent* event); |
| 272 | bool (QKeyEvent* event); |
| 273 | bool onCompletionInputMethod(QInputMethodEvent *event); |
| 274 | |
| 275 | TipType getTipType(QPoint point, QSynedit::BufferCoord& pos); |
| 276 | void cancelHint(); |
| 277 | QString getFileHint(const QString& s); |
| 278 | QString getParserHint(const QStringList& expression,const QString& s, int line); |
| 279 | void showDebugHint(const QString& s,int line); |
| 280 | QString getErrorHint(const PSyntaxIssue& issue); |
| 281 | QString getHintForFunction(const PStatement& statement, |
| 282 | const QString& filename, int line); |
| 283 | |
| 284 | void updateFunctionTip(bool showTip); |
| 285 | void clearUserCodeInTabStops(); |
| 286 | void popUserCodeInTabStops(); |
| 287 | void onExportedFormatToken(QSynedit::PHighlighter syntaxHighlighter, int Line, int column, const QString& token, |
| 288 | QSynedit::PHighlighterAttribute &attr); |
| 289 | void onScrollBarValueChanged(); |
| 290 | private: |
| 291 | QByteArray mEncodingOption; // the encoding type set by the user |
| 292 | QByteArray mFileEncoding; // the real encoding of the file (auto detected) |
| 293 | QString mFilename; |
| 294 | QTabWidget* mParentPageControl; |
| 295 | bool mInProject; |
| 296 | bool mIsNew; |
| 297 | QMap<int,PSyntaxIssueList> mSyntaxIssues; |
| 298 | QColor mSyntaxErrorColor; |
| 299 | QColor mSyntaxWarningColor; |
| 300 | QColor mActiveBreakpointForegroundColor; |
| 301 | QColor mActiveBreakpointBackgroundColor; |
| 302 | QColor mBreakpointForegroundColor; |
| 303 | QColor mBreakpointBackgroundColor; |
| 304 | QColor mCurrentHighlighWordForeground; |
| 305 | QColor mCurrentHighlighWordBackground; |
| 306 | int mSyntaxErrorLine; |
| 307 | int mLineCount; |
| 308 | int mGutterClickedLine; |
| 309 | QSet<int> mBreakpointLines; |
| 310 | QSet<int> mBookmarkLines; |
| 311 | int mActiveBreakpointLine; |
| 312 | PCppParser mParser; |
| 313 | std::shared_ptr<CodeCompletionPopup> ; |
| 314 | std::shared_ptr<HeaderCompletionPopup> ; |
| 315 | int mLastIdCharPressed; |
| 316 | bool mUseCppSyntax; |
| 317 | QString mCurrentWord; |
| 318 | QString mCurrentDebugTipWord; |
| 319 | TipType mCurrentTipType; |
| 320 | QString mOldHighlightedWord; |
| 321 | QString mCurrentHighlightedWord; |
| 322 | QDateTime mHideTime; |
| 323 | |
| 324 | bool mSaving; |
| 325 | bool mCurrentLineModified; |
| 326 | int mXOffsetSince; |
| 327 | int mTabStopBegin; |
| 328 | int mTabStopEnd; |
| 329 | int mTabStopY; |
| 330 | bool mCanAutoSave; |
| 331 | QString mLineBeforeTabStop; |
| 332 | QString mLineAfterTabStop; |
| 333 | QList<PTabStop> mUserCodeInTabStops; |
| 334 | QSynedit::BufferCoord mHighlightCharPos1; |
| 335 | QSynedit::BufferCoord mHighlightCharPos2; |
| 336 | std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > mStatementColors; |
| 337 | QTimer mFunctionTipTimer; |
| 338 | int mHoverModifiedLine; |
| 339 | |
| 340 | // QWidget interface |
| 341 | protected: |
| 342 | void wheelEvent(QWheelEvent *event) override; |
| 343 | void focusInEvent(QFocusEvent *event) override; |
| 344 | void focusOutEvent(QFocusEvent *event) override; |
| 345 | void keyPressEvent(QKeyEvent *event) override; |
| 346 | |
| 347 | // SynEdit interface |
| 348 | protected: |
| 349 | void onGutterPaint(QPainter &painter, int aLine, int X, int Y) override; |
| 350 | void onGetEditingAreas(int Line, QSynedit::EditingAreaList &areaList) override; |
| 351 | |
| 352 | // SynEdit interface |
| 353 | protected: |
| 354 | bool onGetSpecialLineColors(int Line, QColor &foreground, QColor &backgroundColor) override; |
| 355 | |
| 356 | // SynEdit interface |
| 357 | protected: |
| 358 | void onPreparePaintHighlightToken(int line, int aChar, const QString &token, QSynedit::PHighlighterAttribute attr, QSynedit::FontStyles &style, QColor &foreground, QColor &background) override; |
| 359 | |
| 360 | // QObject interface |
| 361 | public: |
| 362 | bool event(QEvent *event) override; |
| 363 | |
| 364 | // QWidget interface |
| 365 | void setInProject(bool newInProject); |
| 366 | |
| 367 | bool useCppSyntax() const; |
| 368 | void setUseCppSyntax(bool newUseCppSyntax); |
| 369 | |
| 370 | const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &statementColors() const; |
| 371 | void setStatementColors(const std::shared_ptr<QHash<StatementKind, std::shared_ptr<ColorSchemeItem> > > &newStatementColors); |
| 372 | |
| 373 | const QDateTime &hideTime() const; |
| 374 | void setHideTime(const QDateTime &newHideTime); |
| 375 | |
| 376 | bool canAutoSave() const; |
| 377 | void setCanAutoSave(bool newCanAutoSave); |
| 378 | |
| 379 | protected: |
| 380 | void mouseReleaseEvent(QMouseEvent *event) override; |
| 381 | void inputMethodEvent(QInputMethodEvent *) override; |
| 382 | void closeEvent(QCloseEvent *event) override; |
| 383 | |
| 384 | // QWidget interface |
| 385 | protected: |
| 386 | void showEvent(QShowEvent *event) override; |
| 387 | void hideEvent(QHideEvent *event) override; |
| 388 | |
| 389 | // QWidget interface |
| 390 | protected: |
| 391 | void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; |
| 392 | }; |
| 393 | |
| 394 | QString getWordAtPosition(QSynedit::SynEdit* editor, |
| 395 | const QSynedit::BufferCoord& p, |
| 396 | QSynedit::BufferCoord& pWordBegin, |
| 397 | QSynedit::BufferCoord& pWordEnd, |
| 398 | Editor::WordPurpose purpose); |
| 399 | |
| 400 | |
| 401 | #endif // EDITOR_H |
| 402 | |