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 | |