From fcd640b0e3853c5aba43faa0a3cf30efa06a0356 Mon Sep 17 00:00:00 2001 From: "royqh1979@gmail.com" Date: Fri, 14 May 2021 23:56:43 +0800 Subject: [PATCH] work save --- RedPandaIDE/RedPandaIDE.pro | 33 +- RedPandaIDE/editor.cpp | 8 +- RedPandaIDE/qsynedit/CodeFolding.cpp | 112 ++ RedPandaIDE/qsynedit/CodeFolding.h | 80 + RedPandaIDE/qsynedit/Constants.cpp | 6 + RedPandaIDE/qsynedit/Constants.h | 163 ++ RedPandaIDE/qsynedit/KeyStrokes.cpp | 243 +++ RedPandaIDE/qsynedit/KeyStrokes.h | 225 +++ RedPandaIDE/qsynedit/MiscClasses.cpp | 480 +++++ RedPandaIDE/qsynedit/MiscClasses.h | 183 ++ RedPandaIDE/qsynedit/MiscProcs.cpp | 533 +++++ RedPandaIDE/qsynedit/MiscProcs.h | 90 + RedPandaIDE/qsynedit/SynEdit.cpp | 1728 +++++++++++++++++ RedPandaIDE/qsynedit/SynEdit.h | 426 ++++ RedPandaIDE/qsynedit/TextBuffer.cpp | 906 +++++++++ RedPandaIDE/qsynedit/TextBuffer.h | 228 +++ RedPandaIDE/qsynedit/TextPainter.cpp | 130 ++ RedPandaIDE/qsynedit/TextPainter.h | 52 + RedPandaIDE/qsynedit/Types.h | 36 + RedPandaIDE/qsynedit/highlighter/base.cpp | 228 +++ RedPandaIDE/qsynedit/highlighter/base.h | 146 ++ .../qsynedit/highlighter/composition.cpp | 93 + .../qsynedit/highlighter/composition.h | 62 + RedPandaIDE/qsynedit/highlighter/cpp.cpp | 1572 +++++++++++++++ RedPandaIDE/qsynedit/highlighter/cpp.h | 212 ++ 25 files changed, 7966 insertions(+), 9 deletions(-) create mode 100644 RedPandaIDE/qsynedit/CodeFolding.cpp create mode 100644 RedPandaIDE/qsynedit/CodeFolding.h create mode 100644 RedPandaIDE/qsynedit/Constants.cpp create mode 100644 RedPandaIDE/qsynedit/Constants.h create mode 100644 RedPandaIDE/qsynedit/KeyStrokes.cpp create mode 100644 RedPandaIDE/qsynedit/KeyStrokes.h create mode 100644 RedPandaIDE/qsynedit/MiscClasses.cpp create mode 100644 RedPandaIDE/qsynedit/MiscClasses.h create mode 100644 RedPandaIDE/qsynedit/MiscProcs.cpp create mode 100644 RedPandaIDE/qsynedit/MiscProcs.h create mode 100644 RedPandaIDE/qsynedit/SynEdit.cpp create mode 100644 RedPandaIDE/qsynedit/SynEdit.h create mode 100644 RedPandaIDE/qsynedit/TextBuffer.cpp create mode 100644 RedPandaIDE/qsynedit/TextBuffer.h create mode 100644 RedPandaIDE/qsynedit/TextPainter.cpp create mode 100644 RedPandaIDE/qsynedit/TextPainter.h create mode 100644 RedPandaIDE/qsynedit/Types.h create mode 100644 RedPandaIDE/qsynedit/highlighter/base.cpp create mode 100644 RedPandaIDE/qsynedit/highlighter/base.h create mode 100644 RedPandaIDE/qsynedit/highlighter/composition.cpp create mode 100644 RedPandaIDE/qsynedit/highlighter/composition.h create mode 100644 RedPandaIDE/qsynedit/highlighter/cpp.cpp create mode 100644 RedPandaIDE/qsynedit/highlighter/cpp.h diff --git a/RedPandaIDE/RedPandaIDE.pro b/RedPandaIDE/RedPandaIDE.pro index 69518282..3805712f 100644 --- a/RedPandaIDE/RedPandaIDE.pro +++ b/RedPandaIDE/RedPandaIDE.pro @@ -17,6 +17,17 @@ SOURCES += \ editorlist.cpp \ main.cpp \ mainwindow.cpp \ + qsynedit/CodeFolding.cpp \ + qsynedit/Constants.cpp \ + qsynedit/KeyStrokes.cpp \ + qsynedit/MiscClasses.cpp \ + qsynedit/MiscProcs.cpp \ + qsynedit/SynEdit.cpp \ + qsynedit/TextBuffer.cpp \ + qsynedit/TextPainter.cpp \ + qsynedit/highlighter/base.cpp \ + qsynedit/highlighter/composition.cpp \ + qsynedit/highlighter/cpp.cpp \ settingsdialog/compilersetdirectorieswidget.cpp \ settingsdialog/compilersetoptionwidget.cpp \ settings.cpp \ @@ -34,6 +45,18 @@ HEADERS += \ editor.h \ editorlist.h \ mainwindow.h \ + qsynedit/CodeFolding.h \ + qsynedit/Constants.h \ + qsynedit/KeyStrokes.h \ + qsynedit/MiscClasses.h \ + qsynedit/MiscProcs.h \ + qsynedit/SynEdit.h \ + qsynedit/TextBuffer.h \ + qsynedit/TextPainter.h \ + qsynedit/Types.h \ + qsynedit/highlighter/base.h \ + qsynedit/highlighter/composition.h \ + qsynedit/highlighter/cpp.h \ settingsdialog/compilersetdirectorieswidget.h \ settingsdialog/compilersetoptionwidget.h \ settings.h \ @@ -61,9 +84,9 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin RESOURCES += \ icons.qrc -win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../QScintilla/src/release/ -lqscintilla2_qt5d -else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../QScintilla/src/debug/ -lqscintilla2_qt5d -else:unix: LIBS += -L$$OUT_PWD/../../QScintilla/src/ -lqscintilla2_qt5d +#win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../../QScintilla/src/release/ -lqscintilla2_qt5d +#else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../../QScintilla/src/debug/ -lqscintilla2_qt5d +#else:unix: LIBS += -L$$OUT_PWD/../../QScintilla/src/ -lqscintilla2_qt5d -INCLUDEPATH += $$PWD/../../QScintilla/src -DEPENDPATH += $$PWD/../../QScintilla/src +#INCLUDEPATH += $$PWD/../../QScintilla/src +#DEPENDPATH += $$PWD/../../QScintilla/src diff --git a/RedPandaIDE/editor.cpp b/RedPandaIDE/editor.cpp index 91ca4930..95fd9b5b 100644 --- a/RedPandaIDE/editor.cpp +++ b/RedPandaIDE/editor.cpp @@ -89,10 +89,10 @@ Editor::Editor(QWidget *parent, const QString& filename, // SLOT(on_margin_clicked(int, int, Qt::KeyboardModifiers))); //markers - markerDefine(QsciScintilla::CircledPlus, ErrorMarker); - setMarkerForegroundColor(QColor("BLACK"),ErrorMarker); - setMarkerBackgroundColor(QColor("RED"),ErrorMarker); - markerAdd(1,ErrorMarker); +// markerDefine(QsciScintilla::CircledPlus, ErrorMarker); +// setMarkerForegroundColor(QColor("BLACK"),ErrorMarker); +// setMarkerBackgroundColor(QColor("RED"),ErrorMarker); +// markerAdd(1,ErrorMarker); // connect will fail if use new function pointer syntax // connect(this, &QsciScintilla::modificationChanged, diff --git a/RedPandaIDE/qsynedit/CodeFolding.cpp b/RedPandaIDE/qsynedit/CodeFolding.cpp new file mode 100644 index 00000000..4ad2cbb8 --- /dev/null +++ b/RedPandaIDE/qsynedit/CodeFolding.cpp @@ -0,0 +1,112 @@ +#include "CodeFolding.h" +#include "Constants.h" + +int SynEditFoldRegions::count() +{ + return fRegions.size(); +} + +int SynEditFoldRegions::add(bool addEnding, const QChar &openSymbol, const QChar &closeSymbol, const QString &highlight) +{ + PSynEditFoldRegion region = std::make_shared(); + region->addEnding = addEnding; + region->openSymbol = openSymbol; + region->closeSymbol = closeSymbol; + region->highlight = highlight; + fRegions.push_back(region); +} + +PSynEditFoldRegion SynEditFoldRegions::get(int index) +{ + return fRegions.at(index); +} + +SynEditCodeFolding::SynEditCodeFolding(): + indentGuides(true), + showCollapsedLine(true), + collapsedLineColor(QColor("black")), + folderBarLinesColor(QColor("black")), + indentGuidesColor("gray") +{ + foldRegions.add(true,'{','}',SYNS_AttrSymbol); +} + + +bool SynEditFoldRange::parentCollapsed() +{ + PSynEditFoldRange parentFold = parent; + // Find first parent that is collapsed + while (parentFold) { + if (parentFold->collapsed) { + return true; + } + parentFold = parentFold->parent; + } + return false; +} + +void SynEditFoldRange::move(int count) +{ + fromLine += count; + toLine += count; +} + +SynEditFoldRange::SynEditFoldRange(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine): + fromLine(aFromLine), + toLine(aToLine), + linesCollapsed(0), + collapsed(false), + allFoldRanges(aAllFold), + foldRegion(aFoldRegion), + hintMarkLeft(0), + parent(aParent) +{ + subFoldRanges = std::make_shared(); +} + + +PSynEditFoldRange SynEditFoldRanges::range(int index) +{ + return mRanges[index]; +} + +void SynEditFoldRanges::clear() +{ + mRanges.clear(); +} + +int SynEditFoldRanges::count() +{ + return mRanges.size(); +} + +SynEditFoldRanges::SynEditFoldRanges() +{ + +} + +PSynEditFoldRange SynEditFoldRanges::addByParts(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine) +{ + PSynEditFoldRange range=std::make_shared(aParent,aAllFold, aFromLine,aFoldRegion,aToLine); + return range; +} + +void SynEditFoldRanges::insert(int index, PSynEditFoldRange range) +{ + mRanges.insert(index,range); +} + +int SynEditFoldRanges::remove(int index) +{ + mRanges.remove(index); +} + +void SynEditFoldRanges::add(PSynEditFoldRange foldRange) +{ + mRanges.push_back(foldRange); +} + +PSynEditFoldRange SynEditFoldRanges::operator[](int index) +{ + return mRanges[index]; +} diff --git a/RedPandaIDE/qsynedit/CodeFolding.h b/RedPandaIDE/qsynedit/CodeFolding.h new file mode 100644 index 00000000..f96d1508 --- /dev/null +++ b/RedPandaIDE/qsynedit/CodeFolding.h @@ -0,0 +1,80 @@ +#ifndef CODEFOLDING_H +#define CODEFOLDING_H +#include +#include +#include +#include + +struct SynEditFoldRegion; +typedef std::shared_ptr PSynEditFoldRegion; + +class SynEditFoldRegions { +private: + std::vector fRegions; +public: + int count(); + int add(bool addEnding, const QChar& openSymbol, const QChar& closeSymbol, const QString& highlight); + PSynEditFoldRegion get(int index); +}; +typedef std::shared_ptr PSynFoldRegions; + + +struct SynEditFoldRegion { + bool addEnding; + SynEditFoldRegions subFoldRegions; + QChar openSymbol; + QChar closeSymbol; + QString highlight; +}; + +struct SynEditCodeFolding { + bool indentGuides; + bool showCollapsedLine; + QColor collapsedLineColor; + QColor folderBarLinesColor; + QColor indentGuidesColor; + SynEditFoldRegions foldRegions; + SynEditCodeFolding(); +}; + +class SynEditFoldRange; +typedef std::shared_ptr PSynEditFoldRange; +class SynEditFoldRanges; +typedef std::shared_ptr PSynEditFoldRanges; + +class SynEditFoldRanges{ +private: + QVector mRanges; +public: + PSynEditFoldRange range(int index); + void clear(); + int count(); + SynEditFoldRanges(); + PSynEditFoldRange addByParts(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, + int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine); + + void insert(int index, PSynEditFoldRange range); + int remove(int index); + void add(PSynEditFoldRange foldRange); + PSynEditFoldRange operator[](int index); +}; + +// A single fold +class SynEditFoldRange { +public: + int fromLine; // Beginning line + int toLine; // End line + int linesCollapsed; // Number of collapsed lines + PSynEditFoldRanges subFoldRanges; // Sub fold ranges + bool collapsed; // Is collapsed? + PSynEditFoldRanges allFoldRanges;// TAllFoldRanges pointer + PSynEditFoldRegion foldRegion; // FoldRegion + int hintMarkLeft; + PSynEditFoldRange parent; + bool parentCollapsed(); + void move(int count); + explicit SynEditFoldRange(PSynEditFoldRange aParent, PSynEditFoldRanges aAllFold, + int aFromLine, PSynEditFoldRegion aFoldRegion, int aToLine); +}; + +#endif // CODEFOLDING_H diff --git a/RedPandaIDE/qsynedit/Constants.cpp b/RedPandaIDE/qsynedit/Constants.cpp new file mode 100644 index 00000000..2712cfd2 --- /dev/null +++ b/RedPandaIDE/qsynedit/Constants.cpp @@ -0,0 +1,6 @@ +#include "Constants.h" +const QSet SynWordBreakChars{'.', ',', ';', ':', + '"', '\'', '!', '?', '[', ']', '(', ')', '{', '}', '^', '-', '=', '+', + '-', '*', '/', '\\', '|'}; +const QChar SynTabChar('\t'); +const QChar SynSpaceGlyph(' '); diff --git a/RedPandaIDE/qsynedit/Constants.h b/RedPandaIDE/qsynedit/Constants.h new file mode 100644 index 00000000..671d42f4 --- /dev/null +++ b/RedPandaIDE/qsynedit/Constants.h @@ -0,0 +1,163 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H +#include +#include + +extern const QSet SynWordBreakChars; +extern const QChar SynTabChar; +extern const QChar SynSpaceGlyph; + +#define MAX_SCROLL 65535 + +#define SYN_ATTR_COMMENT 0 +#define SYN_ATTR_IDENTIFIER 1 +#define SYN_ATTR_KEYWORD 2 +#define SYN_ATTR_STRING 3 +#define SYN_ATTR_WHITESPACE 4 +#define SYN_ATTR_SYMBOL 5 + +// names for highlighter attributes +#define SYNS_AttrAreaAIdentifier "Area A Identifier" +#define SYNS_AttrArrowHead "ArrowHead" +#define SYNS_AttrAsm "Asm" +#define SYNS_AttrAsmComment "Asm Comment" +#define SYNS_AttrAsmKey "Asm Key" +#define SYNS_AttrAssembler "Assembler" +#define SYNS_AttrAttribute "Attribute" +#define SYNS_AttrAttributeName "Attribute Name" +#define SYNS_AttrAttributeValue "Attribute Value" +#define SYNS_AttrBasicTypes "Basic Types" +#define SYNS_AttrBlock "Block" +#define SYNS_AttrBoolean "Boolean value" +#define SYNS_AttrBrackets "Brackets" +#define SYNS_AttrCDATASection "CDATA Section" +#define SYNS_AttrCharacter "Character" +#define SYNS_AttrClass "Class" +#define SYNS_AttrColor "Color Value" +#define SYNS_AttrComment "Comment" +#define SYNS_AttrCondition "Condition" +#define SYNS_AttrConditionalComment "Conditional Comment" +#define SYNS_AttrDataType "Data Type" +#define SYNS_AttrDebugLines "Debugging Lines" +#define SYNS_AttrDefaultPackage "Default Packages" +#define SYNS_AttrDelimitedIdentifier "Delimited Identifier" +#define SYNS_AttrDir "Direction" +#define SYNS_AttrDirections "Directions" +#define SYNS_AttrDirective "Directive" +#define SYNS_AttrDOCTYPESection "DOCTYPE Section" +#define SYNS_AttrDocumentation "Documentation" +#define SYNS_AttrElementName "Element Name" +#define SYNS_AttrEmbedSQL "Embedded SQL" +#define SYNS_AttrEmbedText "Embedded Text" +#define SYNS_AttrEntityReference "Entity Reference" +#define SYNS_AttrEscapeAmpersand "Escape Ampersand" +#define SYNS_AttrEvent "Event" +#define SYNS_AttrException "Exception" +#define SYNS_AttrFirstTri "FirstTri" +#define SYNS_AttrFloat "Float" +#define SYNS_AttrForm "Form" +#define SYNS_AttrFourthTri "FourthTri" +#define SYNS_AttrFunction "Function" +#define SYNS_AttrFunctionParameter "Function Parameter" +#define SYNS_AttrGlobalVariable "Global variable" +#define SYNS_AttrHexadecimal "Hexadecimal" +#define SYNS_AttrIcon "Icon Reference" +#define SYNS_AttrIdentifier "Identifier" +#define SYNS_AttrIllegalChar "Illegal Char" +#define SYNS_AttrInclude "Include" +#define SYNS_AttrIndicator "Indicator Area" +#define SYNS_AttrIndirect "Indirect" +#define SYNS_AttrInvalidSymbol "Invalid Symbol" +#define SYNS_AttrInternalFunction "Internal Function" +#define SYNS_AttrKey "Key" +#define SYNS_AttrLabel "Label" +#define SYNS_AttrLace "Lace" +#define SYNS_AttrLocalVariable "Local Variable" +#define SYNS_AttrLine "Line" +#define SYNS_AttrMacro "Macro" +#define SYNS_AttrMarker "Marker" +#define SYNS_AttrMathMode "Math Mode" +#define SYNS_AttrMessage "Message" +#define SYNS_AttrMiscellaneous "Miscellaneous" +#define SYNS_AttrNamespaceAttrName "Namespace Attribute Name" +#define SYNS_AttrNamespaceAttrValue "Namespace Attribute Value" +#define SYNS_AttrNonReservedKeyword "Non-reserved Keyword" +#define SYNS_AttrNull "Null" +#define SYNS_AttrNumber "Number" +#define SYNS_AttrOctal "Octal" +#define SYNS_AttrOperator "Operator" +#define SYNS_AttrOperatorAndSymbols "Operator And Symbols" +#define SYNS_AttrOpLine "OpLine" +#define SYNS_AttrPLSQL "PL/SQL Reserved Word" +#define SYNS_AttrPragma "Pragma" +#define SYNS_AttrPredefined "Predefined" +#define SYNS_AttrPreprocessor "Preprocessor" +#define SYNS_AttrProcessingInstr "Processing Instruction" +#define SYNS_AttrQuad "Quad" +#define SYNS_AttrQualifier "Qualifier" +#define SYNS_AttrRegister "Register" +#define SYNS_AttrReservedWord "Reserved Word" +#define SYNS_AttrResultValue "Result Value" +#define SYNS_AttrRoundBracket "Round Bracket" +#define SYNS_AttrRpl "Rpl" +#define SYNS_AttrRplKey "Rpl Key" +#define SYNS_AttrRplComment "Rpl Comment" +#define SYNS_AttrSASM "SASM" +#define SYNS_AttrSASMComment "SASM Comment" +#define SYNS_AttrSASMKey "SASM Key" +#define SYNS_AttrSecondReservedWord "Second Reserved Word" +#define SYNS_AttrSecondTri "SecondTri" +#define SYNS_AttrSection "Section" +#define SYNS_AttrSequence "Sequence Number Area" +#define SYNS_AttrShape "Shape" +#define SYNS_AttrSingleString "Single Quoted String" +#define SYNS_AttrSpace "Space" +#define SYNS_AttrSpecialVariable "Special Variable" +#define SYNS_AttrSQLKey "SQL Keyword" +#define SYNS_AttrSQLPlus "SQL*Plus Command" +#define SYNS_AttrSquareBracket "Square Bracket" +#define SYNS_AttrString "String" +#define SYNS_AttrStringEscapeSequences "Escape sequences" +#define SYNS_AttrSymbol "Symbol" +#define SYNS_AttrSyntaxError "Syntax Error" +#define SYNS_AttrSystem "System Functions and Variables" +#define SYNS_AttrSystemValue "System Value" +#define SYNS_AttrTagArea "Tag Area" +#define SYNS_AttrTableName "Table Name" +#define SYNS_AttrTerminator "Terminator" +#define SYNS_AttrTeXCommand "TeX Command" +#define SYNS_AttrText "Text" +#define SYNS_AttrTextMathMode "Text in Math Mode" +#define SYNS_AttrThirdTri "ThirdTri" +#define SYNS_AttrTriangle "Triangle" +#define SYNS_AttrUnknownWord "Unknown Word" +#define SYNS_AttrURI "URI" +#define SYNS_AttrUser "User Functions and Variables" +#define SYNS_AttrUserFunction "User Functions" +#define SYNS_AttrValue "Value" +#define SYNS_AttrVariable "Variable" +#define SYNS_AttrVisitedURI "Visited URI" +#define SYNS_AttrVrmlAppearance "Vrml_Appearance" +#define SYNS_AttrVrmlAttribute "Vrml_Attribute" +#define SYNS_AttrVrmlDefinition "Vrml_Definition" +#define SYNS_AttrVrmlEvent "Vrml_Event" +#define SYNS_AttrVrmlGrouping "Vrml_Grouping" +#define SYNS_AttrVrmlInterpolator "Vrml_Interpolator" +#define SYNS_AttrVrmlLight "Vrml_Light" +#define SYNS_AttrVrmlNode "Vrml_Node" +#define SYNS_AttrVrmlParameter "Vrml_Parameter" +#define SYNS_AttrVrmlProto "Vrml_Proto" +#define SYNS_AttrVrmlSensor "Vrml_Sensor" +#define SYNS_AttrVrmlShape "Vrml_Shape" +#define SYNS_AttrVrmlShape_Hint "Vrml_Shape_Hint" +#define SYNS_AttrVrmlTime_dependent "Vrml_Time_dependent" +#define SYNS_AttrVrmlViewpoint "Vrml_Viewpoint" +#define SYNS_AttrVrmlWorldInfo "Vrml_WorldInfo" +#define SYNS_AttrWhitespace "Whitespace" +#define SYNS_AttrX3DDocType "X3DDocType" +#define SYNS_AttrX3DHeader "X3DHeader" +// names of exporter output formats +#define SYNS_ExporterFormatHTML "HTML" +#define SYNS_ExporterFormatRTF "RTF" +#define SYNS_ExporterFormatTeX "TeX" +#endif // CONSTANTS_H diff --git a/RedPandaIDE/qsynedit/KeyStrokes.cpp b/RedPandaIDE/qsynedit/KeyStrokes.cpp new file mode 100644 index 00000000..60d2728d --- /dev/null +++ b/RedPandaIDE/qsynedit/KeyStrokes.cpp @@ -0,0 +1,243 @@ +#include "KeyStrokes.h" + +SynEditKeyStroke::SynEditKeyStroke() +{ + mKey = 0; + mKeyModifiers = Qt::NoModifier; + mKey2 = 0; + mKeyModifiers2 = Qt::NoModifier; + mCommand = SynEditorCommand::ecNone; +} + +QKeySequence SynEditKeyStroke::keySequence() const +{ + if (mKey2 == 0) { + return QKeySequence(mKey + mKeyModifiers); + } else { + return QKeySequence(mKey + mKeyModifiers, mKey2+mKeyModifiers2); + } +} + +void SynEditKeyStroke::setKeySequence(QKeySequence &keySequence) +{ + if (keySequence.isEmpty()<=0) + return; + decodeKey(keySequence[0],mKey,mKeyModifiers); + if (keySequence.count()>1) { + decodeKey(keySequence[1],mKey2,mKeyModifiers2); + } else { + mKey2=0; + mKeyModifiers2=Qt::NoModifier; + } +} + +int SynEditKeyStroke::key() const +{ + return mKey; +} + +void SynEditKeyStroke::setKey(int key) +{ + mKey = key; +} + +Qt::KeyboardModifiers SynEditKeyStroke::keyModifiers() const +{ + return mKeyModifiers; +} + +void SynEditKeyStroke::setKeyModifiers(const Qt::KeyboardModifiers &keyModifiers) +{ + mKeyModifiers = keyModifiers; +} + +int SynEditKeyStroke::key2() const +{ + return mKey2; +} + +void SynEditKeyStroke::setKey2(int key2) +{ + mKey2 = key2; +} + +Qt::KeyboardModifiers SynEditKeyStroke::keyModifiers2() const +{ + return mKeyModifiers2; +} + +void SynEditKeyStroke::setKeyModifiers2(const Qt::KeyboardModifiers &keyModifiers2) +{ + mKeyModifiers2 = keyModifiers2; +} + +SynEditorCommand SynEditKeyStroke::command() const +{ + return mCommand; +} + +void SynEditKeyStroke::setCommand(const SynEditorCommand &command) +{ + mCommand = command; +} + +SynKeyError::SynKeyError(const QString &reason):BaseError(reason) +{ + +} + +PSynEditKeyStroke SynEditKeyStrokes::add(SynEditorCommand command, int key, Qt::KeyboardModifiers modifiers) +{ + PSynEditKeyStroke keyStroke = std::make_shared(); + keyStroke->setKey(key); + keyStroke->setKeyModifiers(modifiers); + keyStroke->setCommand(command); +} + +PSynEditKeyStroke SynEditKeyStrokes::findCommand(SynEditorCommand command) +{ + for (PSynEditKeyStroke& keyStroke:mList) { + if (keyStroke->command() == command) + return keyStroke; + } + return PSynEditKeyStroke(); +} + +PSynEditKeyStroke SynEditKeyStrokes::findKeycode(int key, Qt::KeyboardModifiers modifiers) +{ + for (PSynEditKeyStroke& keyStroke:mList) { + if (keyStroke->key() == key && keyStroke->keyModifiers()==modifiers && keyStroke->key2()==0) + return keyStroke; + } + return PSynEditKeyStroke(); +} + +PSynEditKeyStroke SynEditKeyStrokes::findKeycode2(int key, Qt::KeyboardModifiers modifiers, + int key2, Qt::KeyboardModifiers modifiers2) +{ + for (PSynEditKeyStroke& keyStroke:mList) { + if (keyStroke->key() == key && keyStroke->keyModifiers()==modifiers && keyStroke->key2()==key2 + && keyStroke->keyModifiers2() ==modifiers2) + return keyStroke; + } + return PSynEditKeyStroke(); +} + +PSynEditKeyStroke SynEditKeyStrokes::findKeySequence(const QKeySequence &keySeq) +{ + switch (keySeq.count()) { + case 1: { + int key; + Qt::KeyboardModifiers modifiers; + decodeKey(keySeq[0],key,modifiers); + return findKeycode(key,modifiers); + } + case 2: + case 3: + case 4: + { + int key; + Qt::KeyboardModifiers modifiers; + int key2; + Qt::KeyboardModifiers modifiers2; + decodeKey(keySeq[0],key,modifiers); + decodeKey(keySeq[1],key2,modifiers2); + return findKeycode2(key,modifiers,key2,modifiers2); + } + default: + return PSynEditKeyStroke(); + } +} + +void SynEditKeyStrokes::clear() +{ + return mList.clear(); +} + +void SynEditKeyStrokes::resetDefaults() +{ + clear(); + add(SynEditorCommand::ecUp, Qt::Key_Up, Qt::NoModifier); + add(SynEditorCommand::ecSelUp, Qt::Key_Up, Qt::ShiftModifier); + add(SynEditorCommand::ecScrollUp, Qt::Key_Up, Qt::ControlModifier); + add(SynEditorCommand::ecDown, Qt::Key_Down, Qt::NoModifier); + add(SynEditorCommand::ecSelDown, Qt::Key_Down, Qt::ShiftModifier); + add(SynEditorCommand::ecScrollDown, Qt::Key_Down, Qt::ControlModifier); + add(SynEditorCommand::ecLeft, Qt::Key_Left, Qt::NoModifier); + add(SynEditorCommand::ecSelLeft, Qt::Key_Left, Qt::ShiftModifier); + add(SynEditorCommand::ecWordLeft, Qt::Key_Left, Qt::ControlModifier); + add(SynEditorCommand::ecSelWordLeft, Qt::Key_Left, Qt::ShiftModifier|Qt::ControlModifier); + add(SynEditorCommand::ecRight, Qt::Key_Right, Qt::NoModifier); + add(SynEditorCommand::ecSelRight, Qt::Key_Right, Qt::ShiftModifier); + add(SynEditorCommand::ecWordRight, Qt::Key_Right, Qt::ControlModifier); + add(SynEditorCommand::ecSelWordRight, Qt::Key_Right, Qt::ShiftModifier|Qt::ControlModifier); + add(SynEditorCommand::ecPageDown, Qt::Key_PageDown, Qt::NoModifier); + add(SynEditorCommand::ecSelPageDown, Qt::Key_PageDown, Qt::ShiftModifier); + add(SynEditorCommand::ecPageBottom, Qt::Key_PageDown, Qt::ControlModifier); + add(SynEditorCommand::ecSelPageBottom, Qt::Key_PageDown, Qt::ShiftModifier|Qt::ControlModifier); + add(SynEditorCommand::ecPageUp, Qt::Key_PageUp, Qt::NoModifier); + add(SynEditorCommand::ecSelPageUp, Qt::Key_PageUp, Qt::ShiftModifier); + add(SynEditorCommand::ecPageTop, Qt::Key_PageUp, Qt::ControlModifier); + add(SynEditorCommand::ecSelPageTop, Qt::Key_PageUp, Qt::ShiftModifier|Qt::ControlModifier); + add(SynEditorCommand::ecLineStart, Qt::Key_Home, Qt::NoModifier); + add(SynEditorCommand::ecSelLineStart, Qt::Key_Home, Qt::ShiftModifier); + add(SynEditorCommand::ecEditorTop, Qt::Key_Home, Qt::ControlModifier); + add(SynEditorCommand::ecSelEditorTop, Qt::Key_Home, Qt::ShiftModifier|Qt::ControlModifier); + add(SynEditorCommand::ecLineEnd, Qt::Key_End, Qt::NoModifier); + add(SynEditorCommand::ecSelLineEnd, Qt::Key_End, Qt::ShiftModifier); + add(SynEditorCommand::ecEditorBottom, Qt::Key_End, Qt::ControlModifier); + add(SynEditorCommand::ecSelEditorBottom, Qt::Key_End, Qt::ShiftModifier|Qt::ControlModifier); + add(SynEditorCommand::ecToggleMode, Qt::Key_Insert, Qt::ShiftModifier); + add(SynEditorCommand::ecCopy, Qt::Key_Insert, Qt::ControlModifier); + add(SynEditorCommand::ecCut, Qt::Key_Delete, Qt::ShiftModifier); + add(SynEditorCommand::ecPaste, Qt::Key_Insert, Qt::ShiftModifier); + add(SynEditorCommand::ecDeleteChar, Qt::Key_Delete, Qt::NoModifier); + add(SynEditorCommand::ecDeleteLastChar, Qt::Key_Backspace, Qt::NoModifier); + add(SynEditorCommand::ecDeleteLastChar, Qt::Key_Backspace, Qt::ShiftModifier); + add(SynEditorCommand::ecDeleteLastWord, Qt::Key_Backspace, Qt::ControlModifier); + add(SynEditorCommand::ecUndo, Qt::Key_Backspace, Qt::AltModifier); + add(SynEditorCommand::ecRedo, Qt::Key_Backspace, Qt::AltModifier|Qt::ShiftModifier); + add(SynEditorCommand::ecLineBreak, Qt::Key_Return, Qt::NoModifier); + add(SynEditorCommand::ecLineBreak, Qt::Key_Return, Qt::ShiftModifier); + add(SynEditorCommand::ecTab, Qt::Key_Tab, Qt::NoModifier); + add(SynEditorCommand::ecShiftTab, Qt::Key_Tab, Qt::ShiftModifier); + add(SynEditorCommand::ecContextHelp, Qt::Key_F1, Qt::NoModifier); + + add(SynEditorCommand::ecSelectAll, Qt::Key_A, Qt::ControlModifier); + add(SynEditorCommand::ecCopy, Qt::Key_C, Qt::ControlModifier); + add(SynEditorCommand::ecPaste, Qt::Key_V, Qt::ControlModifier); + add(SynEditorCommand::ecCut, Qt::Key_X, Qt::ControlModifier); + add(SynEditorCommand::ecBlockIndent, Qt::Key_I, Qt::ControlModifier|Qt::ShiftModifier); + add(SynEditorCommand::ecBlockUnindent, Qt::Key_U, Qt::ControlModifier|Qt::ShiftModifier); + add(SynEditorCommand::ecLineBreak, Qt::Key_M, Qt::ControlModifier); + add(SynEditorCommand::ecInsertLine, Qt::Key_N, Qt::ControlModifier); + add(SynEditorCommand::ecDeleteWord, Qt::Key_T, Qt::ControlModifier); + add(SynEditorCommand::ecDeleteLine, Qt::Key_Y, Qt::ControlModifier); + add(SynEditorCommand::ecDeleteEOL, Qt::Key_Y, Qt::ControlModifier|Qt::ShiftModifier); + add(SynEditorCommand::ecUndo, Qt::Key_Z, Qt::ControlModifier); + add(SynEditorCommand::ecRedo, Qt::Key_Z, Qt::ControlModifier|Qt::ShiftModifier); + add(SynEditorCommand::ecGotoMarker0, Qt::Key_0, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker1, Qt::Key_1, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker2, Qt::Key_2, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker3, Qt::Key_3, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker4, Qt::Key_4, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker5, Qt::Key_5, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker6, Qt::Key_6, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker7, Qt::Key_7, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker8, Qt::Key_8, Qt::ControlModifier); + add(SynEditorCommand::ecGotoMarker9, Qt::Key_9, Qt::ControlModifier); + add(SynEditorCommand::ecSetMarker0, Qt::Key_0, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker1, Qt::Key_1, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker2, Qt::Key_2, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker3, Qt::Key_3, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker4, Qt::Key_4, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker5, Qt::Key_5, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker6, Qt::Key_6, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker7, Qt::Key_7, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker8, Qt::Key_8, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecSetMarker9, Qt::Key_9, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecNormalSelect, Qt::Key_N, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecColumnSelect, Qt::Key_C, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecLineSelect, Qt::Key_L, Qt::ControlModifier | Qt::ShiftModifier); + add(SynEditorCommand::ecMatchBracket, Qt::Key_B, Qt::ControlModifier | Qt::ShiftModifier); +} diff --git a/RedPandaIDE/qsynedit/KeyStrokes.h b/RedPandaIDE/qsynedit/KeyStrokes.h new file mode 100644 index 00000000..0f4d058d --- /dev/null +++ b/RedPandaIDE/qsynedit/KeyStrokes.h @@ -0,0 +1,225 @@ +#ifndef SYNEDITKEYSTROKE_H +#define SYNEDITKEYSTROKE_H + +#include "../utils.h" +#include +#include +#include +//**************************************************************************** +// NOTE! If you add an editor command, you must also update the +// EditorCommandStrs constant array in implementation section below, or the +// command will not show up in the IDE. +//**************************************************************************** + +// "Editor Commands". Key strokes are translated from a table into these +// I used constants instead of a set so that additional commands could be +// added in descendants (you can't extend a set) + +// There are two ranges of editor commands: the ecViewXXX commands are always +// valid, while the ecEditXXX commands are ignored when the editor is in +// read-only mode + +enum class SynEditorCommand { + ecNone = 0, // Nothing. Useful for user event to handle command + ecViewCommandFirst = 0, + ecViewCommandLast = 500, + ecEditCommandFirst = 501, + ecEditCommandLast = 1000, + + ecLeft = 1, // Move cursor left one char + ecRight = 2, // Move cursor right one char + ecUp = 3, // Move cursor up one line + ecDown = 4, // Move cursor down one line + ecWordLeft = 5, // Move cursor left one word + ecWordRight = 6, // Move cursor right one word + ecLineStart = 7, // Move cursor to beginning of line + ecLineEnd = 8, // Move cursor to end of line + ecPageUp = 9, // Move cursor up one page + ecPageDown = 10, // Move cursor down one page + ecPageLeft = 11, // Move cursor right one page + ecPageRight = 12, // Move cursor left one page + ecPageTop = 13, // Move cursor to top of page + ecPageBottom = 14, // Move cursor to bottom of page + ecEditorTop = 15, // Move cursor to absolute beginning + ecEditorBottom = 16, // Move cursor to absolute end + ecGotoXY = 17, // Move cursor to specific coordinates, Data = PPoint + +//****************************************************************************** +// Maybe the command processor should just take a boolean that signifies if +// selection is affected or not? +//****************************************************************************** + + ecSelection = 100, // Add this to ecXXX command to get equivalent + // command, but with selection enabled. This is not + // a command itself. +// Same as commands above, except they affect selection, too + ecSelLeft = ecLeft + ecSelection, + ecSelRight = ecRight + ecSelection, + ecSelUp = ecUp + ecSelection, + ecSelDown = ecDown + ecSelection, + ecSelWordLeft = ecWordLeft + ecSelection, + ecSelWordRight = ecWordRight + ecSelection, + ecSelLineStart = ecLineStart + ecSelection, + ecSelLineEnd = ecLineEnd + ecSelection, + ecSelPageUp = ecPageUp + ecSelection, + ecSelPageDown = ecPageDown + ecSelection, + ecSelPageLeft = ecPageLeft + ecSelection, + ecSelPageRight = ecPageRight + ecSelection, + ecSelPageTop = ecPageTop + ecSelection, + ecSelPageBottom = ecPageBottom + ecSelection, + ecSelEditorTop = ecEditorTop + ecSelection, + ecSelEditorBottom = ecEditorBottom + ecSelection, + ecSelGotoXY = ecGotoXY + ecSelection, // Data = PPoint + + ecSelWord = 198, + ecSelectAll = 199, // Select entire contents of editor, cursor to end + + ecCopy = 201, // Copy selection to clipboard + + ecScrollUp = 211, // Scroll up one line leaving cursor position unchanged. + ecScrollDown = 212, // Scroll down one line leaving cursor position unchanged. + ecScrollLeft = 213, // Scroll left one char leaving cursor position unchanged. + ecScrollRight = 214, // Scroll right one char leaving cursor position unchanged. + + ecInsertMode = 221, // Set insert mode + ecOverwriteMode = 222, // Set overwrite mode + ecToggleMode = 223, // Toggle ins/ovr mode + + ecNormalSelect = 231, // Normal selection mode + ecColumnSelect = 232, // Column selection mode + ecLineSelect = 233, // Line selection mode + + ecMatchBracket = 250, // Go to matching bracket + + ecGotoMarker0 = 301, // Goto marker + ecGotoMarker1 = 302, // Goto marker + ecGotoMarker2 = 303, // Goto marker + ecGotoMarker3 = 304, // Goto marker + ecGotoMarker4 = 305, // Goto marker + ecGotoMarker5 = 306, // Goto marker + ecGotoMarker6 = 307, // Goto marker + ecGotoMarker7 = 308, // Goto marker + ecGotoMarker8 = 309, // Goto marker + ecGotoMarker9 = 310, // Goto marker + ecSetMarker0 = 351, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker1 = 352, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker2 = 353, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker3 = 354, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker4 = 355, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker5 = 356, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker6 = 357, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker7 = 358, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker8 = 359, // Set marker, Data = PPoint - X, Y Pos + ecSetMarker9 = 360, // Set marker, Data = PPoint - X, Y Pos + + ecGotFocus = 480, + ecLostFocus = 481, + + ecContextHelp = 490, // Help on Word, Data = Word + + ecDeleteLastChar = 501, // Delete last char (i.e. backspace key) + ecDeleteChar = 502, // Delete char at cursor (i.e. delete key) + ecDeleteWord = 503, // Delete from cursor to end of word + ecDeleteLastWord = 504, // Delete from cursor to start of word + ecDeleteBOL = 505, // Delete from cursor to beginning of line + ecDeleteEOL = 506, // Delete from cursor to end of line + ecDeleteLine = 507, // Delete current line + ecClearAll = 508, // Delete everything + ecLineBreak = 509, // Break line at current position, move caret to new line + ecInsertLine = 510, // Break line at current position, leave caret + ecChar = 511, // Insert a character at current position + ecDuplicateLine = 512, // Duplicate current line + ecMoveSelUp = 513, // Move selection up + ecMoveSelDown = 514, // Move selection down + ecImeStr = 550, // Insert character(s) from IME + + ecUndo = 601, // Perform undo if available + ecRedo = 602, // Perform redo if available + ecCut = 603, // Cut selection to clipboard + ecPaste = 604, // Paste clipboard to current position + + ecBlockIndent = 610, // Indent selection + ecBlockUnindent = 611, // Unindent selection + ecTab = 612, // Tab key + ecShiftTab = 613, // Shift+Tab key + ecComment = 614, + ecUncomment = 615, + ecToggleComment = 616, + ecCommentInline = 617, + + ecAutoCompletion = 650, + + ecUpperCase = 620, // apply to the current or previous word + ecLowerCase = 621, + ecToggleCase = 622, + ecTitleCase = 623, + ecUpperCaseBlock = 625, // apply to current selection, or current char if no selection + ecLowerCaseBlock = 626, + ecToggleCaseBlock = 627, + + ecString = 630, //Insert a whole string + + ecUserFirst = 1001, // Start of user-defined commands + + //### Code Folding ### + ecCollapse = ecUserFirst + 100, + ecUncollapse = ecUserFirst + 101, + ecCollapseLevel = ecUserFirst + 102, + ecUncollapseLevel = ecUserFirst + 103, + ecCollapseAll = ecUserFirst + 104, + ecUncollapseAll = ecUserFirst + 105, + //### End Code Folding ### +}; + +class SynKeyError: public BaseError { +public: + explicit SynKeyError(const QString& reason); +}; + +class SynEditKeyStroke +{ +public: + explicit SynEditKeyStroke(); + QKeySequence keySequence() const; + void setKeySequence(QKeySequence& keySequence); + int key() const; + void setKey(int key); + + Qt::KeyboardModifiers keyModifiers() const; + void setKeyModifiers(const Qt::KeyboardModifiers &keyModifiers); + + int key2() const; + void setKey2(int key2); + + Qt::KeyboardModifiers keyModifiers2() const; + void setKeyModifiers2(const Qt::KeyboardModifiers &keyModifiers2); + + SynEditorCommand command() const; + void setCommand(const SynEditorCommand &command); + +private: + int mKey; // Virtual keycode, i.e. VK_xxx + Qt::KeyboardModifiers mKeyModifiers; + int mKey2; + Qt::KeyboardModifiers mKeyModifiers2; + SynEditorCommand mCommand; + +}; + +using PSynEditKeyStroke = std::shared_ptr; +using SynEditKeyStrokeList = QList; + +class SynEditKeyStrokes { +public: + PSynEditKeyStroke add(SynEditorCommand command, int key, Qt::KeyboardModifiers modifiers); + PSynEditKeyStroke findCommand(SynEditorCommand command); + PSynEditKeyStroke findKeycode(int key, Qt::KeyboardModifiers modifiers); + PSynEditKeyStroke findKeycode2(int key, Qt::KeyboardModifiers modifiers, + int key2, Qt::KeyboardModifiers modifiers2); + PSynEditKeyStroke findKeySequence(const QKeySequence& keySeq); + void clear(); + void resetDefaults(); + SynEditKeyStrokeList mList; +}; + +#endif // SYNEDITKEYSTROKE_H diff --git a/RedPandaIDE/qsynedit/MiscClasses.cpp b/RedPandaIDE/qsynedit/MiscClasses.cpp new file mode 100644 index 00000000..a06dc737 --- /dev/null +++ b/RedPandaIDE/qsynedit/MiscClasses.cpp @@ -0,0 +1,480 @@ +#include "MiscClasses.h" +#include "algorithm" + +SynGutter::SynGutter(QObject *parent): + QObject(parent) +{ + mFont = QFont("Courier New",9); + mColor= QColorConstants::Svg::lightgray; + mBorderColor = QColorConstants::Transparent; + mWidth = 30; + mShowLineNumbers = true; + mDigitCount = 4; + mLeadingZeros = false; + mLeftOffset = 16; + mRightOffset = 2; + mVisible = true; + mUseFontStyle = false; + mAutoSize = true; + mAutoSizeDigitCount = mDigitCount; + mBorderStyle = SynGutterBorderStyle::Middle; + mLineNumberStart = 1; + mGradient = false; + mGradientStartColor = QColorConstants::Transparent; + mGradientEndColor = QColorConstants::Transparent; + mGradientSteps = 48; +} + +QFont SynGutter::font() const +{ + return mFont; +} + +void SynGutter::setFont(const QFont &font) +{ + if (mFont != font) { + mFont = font; + setChanged(); + } +} + +bool SynGutter::autoSize() const +{ + return mAutoSize; +} + +void SynGutter::setAutoSize(bool value) +{ + if (mAutoSize != value) { + mAutoSize = value; + setChanged(); + } +} + +void SynGutter::setChanged() +{ + emit changed(); +} + +int SynGutter::width() const +{ + return mWidth; +} + +void SynGutter::setWidth(int width) +{ + if (mWidth != width ) { + mWidth = width; + setChanged(); + } +} + +void SynGutter::autoSizeDigitCount(int linesCount) +{ + if (mVisible && mAutoSize && mShowLineNumbers) { + linesCount += (mLineNumberStart - 1); + } + int nDigits = std::max(QString::number(linesCount).length(), mDigitCount); + if (mAutoSizeDigitCount!=nDigits) { + mAutoSizeDigitCount = nDigits; + setChanged(); + } +} + +QString SynGutter::formatLineNumber(int line) +{ + line += (mLineNumberStart - 1); + QString result = QString::number(line); + return QString(mAutoSizeDigitCount - result.length(),'0') + result; +} + +int SynGutter::realGutterWidth(int charWidth) +{ + if (!mVisible) { + return 0; + } + if (mShowLineNumbers) { + return mLeftOffset + mRightOffset + mAutoSizeDigitCount * charWidth + 2; + } + return mWidth; +} + +bool SynGutter::visible() const +{ + return mVisible; +} + +void SynGutter::setVisible(bool visible) +{ + if (mVisible!=visible) { + mVisible = visible; + setChanged(); + } +} + +bool SynGutter::useFontStyle() const +{ + return mUseFontStyle; +} + +void SynGutter::setUseFontStyle(bool useFontStyle) +{ + if (mUseFontStyle!=useFontStyle) { + mUseFontStyle = useFontStyle; + setChanged(); + } +} + +bool SynGutter::showLineNumbers() const +{ + return mShowLineNumbers; +} + +void SynGutter::setShowLineNumbers(bool showLineNumbers) +{ + if (mShowLineNumbers!=showLineNumbers) { + mShowLineNumbers = showLineNumbers; + setChanged(); + } +} + +int SynGutter::rightOffset() const +{ + return mRightOffset; +} + +void SynGutter::setRightOffset(int rightOffset) +{ + int value = std::max(0, rightOffset); + if (mRightOffset != value) { + mRightOffset = value; + setChanged(); + } +} + +int SynGutter::lineNumberStart() const +{ + return mLineNumberStart; +} + +void SynGutter::setLineNumberStart(int lineNumberStart) +{ + int value = std::max(0,lineNumberStart); + if (mLineNumberStart!=value) { + mLineNumberStart = value; + setChanged(); + } +} + +bool SynGutter::zeroStart() +{ + return mLineNumberStart == 0; +} + +int SynGutter::leftOffset() const +{ + return mLeftOffset; +} + +void SynGutter::setLeftOffset(int leftOffset) +{ + int value = std::max(0,leftOffset); + if (mLeftOffset != value) { + mLeftOffset = value; + setChanged(); + } +} + +bool SynGutter::leadingZeros() const +{ + return mLeadingZeros; +} + +void SynGutter::setLeadingZeros(bool value) +{ + if (mLeadingZeros!=value) { + mLeadingZeros = value; + setChanged(); + } +} + +int SynGutter::gradientSteps() const +{ + return mGradientSteps; +} + +void SynGutter::setGradientSteps(int value) +{ + if (mGradientSteps!=value) { + mGradientSteps = value; + if (mGradientSteps<2) + mGradientSteps = 2; + setChanged(); + } +} + +QColor SynGutter::gradientEndColor() const +{ + return mGradientEndColor; +} + +void SynGutter::setGradientEndColor(const QColor &value) +{ + if (mGradientEndColor!=value) { + mGradientEndColor = value; + setChanged(); + } +} + +QColor SynGutter::gradientStartColor() const +{ + return mGradientStartColor; +} + +void SynGutter::setGradientStartColor(const QColor &value) +{ + if (mGradientStartColor!=value) { + mGradientStartColor = value; + setChanged(); + } +} + +bool SynGutter::gradient() const +{ + return mGradient; +} + +void SynGutter::setGradient(bool value) +{ + if (mGradient!=value){ + mGradient = value; + setChanged(); + } +} + +SynGutterBorderStyle SynGutter::borderStyle() const +{ + return mBorderStyle; +} + +void SynGutter::setBorderStyle(const SynGutterBorderStyle &value) +{ + if (mBorderStyle!=value) { + mBorderStyle = value; + setChanged(); + } +} + +int SynGutter::digitCount() const +{ + return mDigitCount; +} + +void SynGutter::setDigitCount(int value) +{ + if (mDigitCount != value ) { + mDigitCount = value; + setChanged(); + } +} + +QColor SynGutter::color() const +{ + return mColor; +} + +void SynGutter::setColor(const QColor &value) +{ + if (mColor!=value) { + mColor = value; + setChanged(); + } +} + +QColor SynGutter::borderColor() const +{ + return mBorderColor; +} + +void SynGutter::setBorderColor(const QColor &value) +{ + if (mBorderColor!=value) { + mBorderColor = value; + setChanged(); + } +} + + +SynEditMark::SynEditMark(QObject *parent) +{ + mBookmarkNum = -1; + +} + +int SynEditMark::Char() const +{ + return mChar; +} + +void SynEditMark::setChar(int value) +{ + if (value != mChar) { + mChar = value; + } +} + +int SynEditMark::image() const +{ + return mImage; +} + +void SynEditMark::setImage(int image) +{ + if (mImage != image) { + mImage = image; + if (mVisible) + emit changed(); + } +} + +bool SynEditMark::visible() const +{ + return mVisible; +} + +void SynEditMark::setVisible(bool visible) +{ + if (mVisible!=visible) { + mVisible = visible; + emit changed(); + } +} + +int SynEditMark::bookmarkNum() const +{ + return mBookmarkNum; +} + +void SynEditMark::setBookmarkNum(int bookmarkNum) +{ + mBookmarkNum = bookmarkNum; +} + +bool SynEditMark::internalImage() const +{ + return mInternalImage; +} + +void SynEditMark::setInternalImage(bool internalImage) +{ + if (mInternalImage!=internalImage) { + mInternalImage = internalImage; + if (mVisible) + emit changed(); + } +} + +bool SynEditMark::isBookmark() const +{ + return (mBookmarkNum>=0); +} + +int SynEditMark::line() const +{ + return mLine; +} + +void SynEditMark::setLine(int line) +{ + if (mLine!=line) { + if (mVisible && mLine>0) + emit changed(); + mLine = line; + if (mVisible && mLine>0) + emit changed(); + } +} + +SynBookMarkOpt::SynBookMarkOpt(QObject *parent) +{ + mDrawBookmarksFirst = true; + mEnableKeys = true; + mGlyphsVisible = true; + mLeftMargin = 2; + mXOffset = 12; +} + +PSynIconList SynBookMarkOpt::bookmarkImages() const +{ + return mBookmarkImages; +} + +void SynBookMarkOpt::setBookmarkImages(const PSynIconList &images) +{ + if (mBookmarkImages != images) { + mBookmarkImages = images; + emit changed(); + } +} + +bool SynBookMarkOpt::drawBookmarksFirst() const +{ + return mDrawBookmarksFirst; +} + +void SynBookMarkOpt::setDrawBookmarksFirst(bool drawBookmarksFirst) +{ + if (mDrawBookmarksFirst != drawBookmarksFirst) { + mDrawBookmarksFirst = drawBookmarksFirst; + emit changed(); + } +} + +bool SynBookMarkOpt::enableKeys() const +{ + return mEnableKeys; +} + +void SynBookMarkOpt::setEnableKeys(bool enableKeys) +{ + mEnableKeys = enableKeys; +} + +bool SynBookMarkOpt::glyphsVisible() const +{ + return mGlyphsVisible; +} + +void SynBookMarkOpt::setGlyphsVisible(bool glyphsVisible) +{ + if (mGlyphsVisible!=glyphsVisible) { + mGlyphsVisible = glyphsVisible; + emit changed(); + } +} + +int SynBookMarkOpt::leftMargin() const +{ + return mLeftMargin; +} + +void SynBookMarkOpt::setLeftMargin(int leftMargin) +{ + if (leftMargin!=mLeftMargin) { + mLeftMargin = leftMargin; + emit changed(); + } +} + +int SynBookMarkOpt::xOffset() const +{ + return mXOffset; +} + +void SynBookMarkOpt::setXOffset(int xOffset) +{ + if (mXOffset!=xOffset) { + mXOffset = xOffset; + emit changed(); + } +} diff --git a/RedPandaIDE/qsynedit/MiscClasses.h b/RedPandaIDE/qsynedit/MiscClasses.h new file mode 100644 index 00000000..a8525e96 --- /dev/null +++ b/RedPandaIDE/qsynedit/MiscClasses.h @@ -0,0 +1,183 @@ +#ifndef MISCCLASSES_H +#define MISCCLASSES_H + +#include +#include +#include +#include "Types.h" + +enum class SynGutterBorderStyle { + None, + Middle, + Right +}; + +class SynGutter : public QObject { + Q_OBJECT +public: + explicit SynGutter(QObject* parent = nullptr); + QFont font() const; + void setFont(const QFont &value); + + bool autoSize() const; + void setAutoSize(bool value); + + QColor borderColor() const; + void setBorderColor(const QColor &value); + + QColor color() const; + void setColor(const QColor &value); + + int digitCount() const; + void setDigitCount(int value); + + SynGutterBorderStyle borderStyle() const; + void setBorderStyle(const SynGutterBorderStyle &value); + + bool gradient() const; + void setGradient(bool value); + + QColor gradientStartColor() const; + void setGradientStartColor(const QColor &value); + + QColor gradientEndColor() const; + void setGradientEndColor(const QColor &value); + + int gradientSteps() const; + void setGradientSteps(int value); + + bool leadingZeros() const; + void setLeadingZeros(bool value); + + + int leftOffset() const; + void setLeftOffset(int leftOffset); + + int lineNumberStart() const; + void setLineNumberStart(int lineNumberStart); + + bool zeroStart(); + int rightOffset() const; + void setRightOffset(int rightOffset); + + bool showLineNumbers() const; + void setShowLineNumbers(bool showLineNumbers); + + bool useFontStyle() const; + void setUseFontStyle(bool useFontStyle); + + bool visible() const; + void setVisible(bool visible); + + int width() const; + void setWidth(int width); + + void autoSizeDigitCount(int linesCount); + QString formatLineNumber(int line); + int realGutterWidth(int charWidth); + +signals: + void changed(); +private: + void setChanged(); +private: + bool mAutoSize; + QColor mBorderColor; + QColor mColor; + int mDigitCount; + QFont mFont; + bool mGradient; + QColor mGradientStartColor; + QColor mGradientEndColor; + int mGradientSteps; + bool mLeadingZeros; + int mLeftOffset; + int mLineNumberStart; + int mRightOffset; + bool mShowLineNumbers; + SynGutterBorderStyle mBorderStyle; + bool mUseFontStyle; + bool mVisible; + int mWidth; + int mAutoSizeDigitCount; +}; + +using PSynGutter = std::shared_ptr; + +class SynEditMark : public QObject { + Q_OBJECT +public: + explicit SynEditMark(QObject* parent = nullptr); + int Char() const; + void setChar(int value); + + int image() const; + void setImage(int image); + + bool visible() const; + void setVisible(bool visible); + + int bookmarkNum() const; + void setBookmarkNum(int bookmarkNum); + + bool internalImage() const; + void setInternalImage(bool internalImage); + + bool isBookmark() const ; + + int line() const; + void setLine(int line); + +signals: + void changed(); +protected: + int mLine; + int mChar; + int mImage; + bool mVisible; + bool mInternalImage; + int mBookmarkNum; + +}; + +using PSynEditMark = std::shared_ptr; + +using SynEditMarkList = QList; + +using PSynEditMarkList = std::shared_ptr; + +class SynBookMarkOpt: public QObject { + Q_OBJECT +public: + explicit SynBookMarkOpt(QObject* parent=nullptr); + PSynIconList bookmarkImages() const; + void setBookmarkImages(const PSynIconList &images); + + bool drawBookmarksFirst() const; + void setDrawBookmarksFirst(bool drawBookmarksFirst); + + bool enableKeys() const; + void setEnableKeys(bool enableKeys); + + bool glyphsVisible() const; + void setGlyphsVisible(bool glyphsVisible); + + int leftMargin() const; + void setLeftMargin(int leftMargin); + + int xOffset() const; + void setXOffset(int xOffset); + +signals: + void changed(); +private: + PSynIconList mBookmarkImages; + bool mDrawBookmarksFirst; + bool mEnableKeys; + bool mGlyphsVisible; + int mLeftMargin; + int mXOffset; +}; + +using PSynBookMarkOpt = std::shared_ptr; +#endif // MISCCLASSES_H diff --git a/RedPandaIDE/qsynedit/MiscProcs.cpp b/RedPandaIDE/qsynedit/MiscProcs.cpp new file mode 100644 index 00000000..c7ff94d0 --- /dev/null +++ b/RedPandaIDE/qsynedit/MiscProcs.cpp @@ -0,0 +1,533 @@ +#include "MiscProcs.h" +#include +#include +#include +#include + +int MinMax(int x, int mi, int ma) +{ + x = std::min(x, ma ); + return std::max( x, mi ); +} + +void SwapInt(int &l, int &r) +{ + int tmp = r; + r = l; + l = tmp; +} + +QPoint MaxPoint(const QPoint &P1, const QPoint &P2) +{ + if ( (P2.y() > P1.y()) || ( (P2.y() == P1.y()) && (P2.x() > P1.x())) ) { + return P2; + } else { + return P1; + } +} + +QPoint MinPoint(const QPoint &P1, const QPoint &P2) +{ + if ( (P2.y() < P1.y()) || ( (P2.y() == P1.y()) && (P2.x() < P1.x())) ) { + return P2; + } else { + return P1; + } +} + +PIntArray GetIntArray(size_t Count, int InitialValue) +{ + return std::make_shared(Count,InitialValue); +} + +void InternalFillRect(QPainter *painter, const QRect &rcPaint, const QColor& color) +{ + painter->fillRect(rcPaint,color); +} + + +bool IsPowerOfTwo(int TabWidth) { + if (TabWidth<2) + return false; + int nW = 2; + do { + if (nW >= TabWidth) + break; + nW <<= 1; + } while (nW<0x10000); + return (nW == TabWidth); +} + +QString ConvertTabs1Ex(const QString &Line, int TabWidth, bool &HasTabs) { + QString Result = Line; // increment reference count only + int nBeforeTab; + if (GetHasTabs(Line, nBeforeTab)) { + QChar* pDest; + HasTabs = true; + pDest = Result.data()+nBeforeTab+1; + // this will make a copy of Line + // We have at least one tab in the string, and the tab width is 1. + // pDest points to the first tab char. We overwrite all tabs with spaces. + while (*pDest!=0) { + if (*pDest == '\t') { + *pDest = ' '; + }; + pDest++; + } + } else + HasTabs = false; + return Result; +} + +QString ConvertTabs1(const QString &Line, int TabWidth) { + bool HasTabs; + return ConvertTabs1Ex(Line, TabWidth, HasTabs); +} + +QString ConvertTabs2nEx(const QString &Line, int TabWidth, bool &HasTabs) { + QString Result = Line; // increment reference count only + int DestLen; + if (GetHasTabs(Line, DestLen)) { + HasTabs = true; + int pSrc = 1 + DestLen; + // We have at least one tab in the string, and the tab width equals 2^n. + // pSrc points to the first tab char in Line. We get the number of tabs + // and the length of the expanded string now. + int TabCount = 0; + int TabMask = (TabWidth - 1) ^ 0x7FFFFFFF; + do { + if (Line[pSrc] == '\t') { + DestLen = (DestLen + TabWidth) & TabMask; + TabCount++; + } else + DestLen ++ ; + } while (pSrc < Line.length()); + // Set the length of the expanded string. + Result.resize(DestLen); + DestLen = 0; + pSrc = 0; + QChar * pDest = Result.data(); + // We use another TabMask here to get the difference to 2^n. + TabMask = TabWidth - 1; + do { + if (Line[pSrc] == '\t') { + int i = TabWidth - (DestLen & TabMask); + DestLen += i; + //This is used for both drawing and other stuff and is meant to be #9 and not #32 + do { + *pDest = '\t'; + pDest ++ ; + i--; + } while (i > 0); + TabCount -- ; + if (TabCount == 0) { + do { + pSrc++ ; + *pDest = Line[pSrc]; + pDest++; + } while (pSrc < Line.length()); + return Result; + } + } else { + *pDest = Line[pSrc]; + pDest ++ ; + DestLen ++; + } + pSrc++; + } while (pSrc < Line.length()); + + } else + HasTabs = false; + return Result; +} + +QString ConvertTabs2n(const QString &Line, int TabWidth) { + bool HasTabs; + return ConvertTabs2nEx(Line, TabWidth, HasTabs); +} + +ConvertTabsProc GetBestConvertTabsProc(int TabWidth) +{ + if (TabWidth < 2) + return &ConvertTabs1; + else if (IsPowerOfTwo(TabWidth)) + return &ConvertTabs2n; + else + return &ConvertTabs; +} + +QString ConvertTabs(const QString &Line, int TabWidth) +{ + bool HasTabs; + return ConvertTabsEx(Line, TabWidth, HasTabs); +} + +ConvertTabsProcEx GetBestConvertTabsProcEx(int TabWidth) +{ + if (TabWidth < 2) + return &ConvertTabs1Ex; + else if (IsPowerOfTwo(TabWidth)) + return &ConvertTabs2nEx; + else + return &ConvertTabsEx; +} + +QString ConvertTabsEx(const QString &Line, int TabWidth, bool &HasTabs) +{ + QString Result = Line; // increment reference count only + int DestLen; + int pSrc; + QChar* pDest; + if (GetHasTabs(Line, DestLen)) { + HasTabs = true; + pSrc = (DestLen+1); + // We have at least one tab in the string, and the tab width is greater + // than 1. pSrc points to the first tab char in Line. We get the number + // of tabs and the length of the expanded string now. + int TabCount = 0; + do { + if (Line[pSrc] == '\t') { + DestLen = DestLen + TabWidth - DestLen % TabWidth; + TabCount++; + } else { + DestLen ++; + } + pSrc++; + } while (pSrc 1) { + if ((TabWidth <= 1) || !GetHasTabs(Line, iChar) ) { + Result = Index; + } else { + if (iChar + 1 >= Index) { + Result = Index; + } else { + // iChar is number of chars before first Tab + Result = iChar; + // Index is *not* zero-based + iChar++; + Index -= iChar; + int pNext = iChar; + while (Index > 0) { + if (pNext>=Line.length()) { + Result += Index; + break; + } + if (Line[pNext] == '\t') { + Result += TabWidth; + Result -= Result % TabWidth; + } else + Result++; + Index--; + pNext++; + } + // done with zero-based computation + Result++; + } + } + } else + Result = 1; + return Result; +} + +int CaretPos2CharIndex(int Position, int TabWidth, const QString &Line, bool &InsideTabChar) +{ + int Result; + int iPos; + InsideTabChar = false; + if (Position > 1) { + if ( (TabWidth <= 1) || !GetHasTabs(Line, iPos) ) { + Result = Position; + } else { + if (iPos + 1 >= Position) { + Result = Position; + } else { + // iPos is number of chars before first #9 + Result = iPos + 1; + int pNext = Result; + // for easier computation go zero-based (mod-operation) + Position -=1; + while (iPos < Position) { + if (pNext>=Line.length()) + break; + if (Line[pNext] == '\t') { + iPos+=TabWidth; + iPos-=iPos % TabWidth; + if (iPos > Position) { + InsideTabChar = true; + break; + } + } else + iPos++; + Result++; + pNext++; + } + } + } + } else + Result = Position; + return Result; +} + +int StrScanForCharInSet(const QString &Line, int Start, const QSet& AChars) +{ + for (int i=Start;i &AChars) +{ + for (int i=Line.size()-1;i>=Start;i--) { + if (AChars.contains(Line[i])) { + return i; + } + } + return -1; +} + +int GetEOL(const QString &Line, int start) +{ + if (start<0 || start>=Line.size()) { + return start; + } + for (int i=start;i& Params, + SynHighlighterList& HighlighterList) { + bool Result = true; + if (HighlighterList.indexOf(Highlighter)>0) { + if (SkipDuplicates) + return Result; + } else { + HighlighterList.append(Highlighter); + } + if (Highlighter->getClass() == SynHighlighterClass::Composition) { + //todo: handle composition highlighter + } else if (Highlighter) { + for (PSynHighlighterAttribute pAttr: Highlighter->attributes()){ + QString UniqueAttriName = Highlighter->getName() + + HighlighterList.indexOf(Highlighter) + '.' + + pAttr->name(); + Result = highlighterAttriProc(Highlighter, pAttr, + UniqueAttriName, Params); + if (!Result) + break; + } + } + return Result; +} + +bool EnumHighlighterAttris(PSynHighlighter Highlighter, bool SkipDuplicates, + HighlighterAttriProc highlighterAttriProc, + std::initializer_list Params) +{ + if (!Highlighter || !highlighterAttriProc) { + return false; + } + + SynHighlighterList HighlighterList; + return InternalEnumHighlighterAttris(Highlighter, SkipDuplicates, + highlighterAttriProc, Params, HighlighterList); +} + +uint16_t fcstab[] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +uint16_t CalcFCS(unsigned char *ABuf, int ABufSize) +{ + uint16_t CurFCS = 0xffff; + unsigned char* P = ABuf; + while (ABufSize>0) { + CurFCS = (CurFCS >> 8) ^ fcstab[(CurFCS ^ *P) & 0xff]; + ABufSize -- ; + P ++ ; + } + return CurFCS; +} + +void SynDrawGradient(QPaintDevice *ACanvas, const QColor &AStartColor, const QColor &AEndColor, int , const QRect &ARect, bool AHorizontal) +{ + QPainter painter(ACanvas); + if (AHorizontal) { + int Size = ARect.right() - ARect.left(); + QLinearGradient gradient(0,0,Size,0); + gradient.setColorAt(0,AStartColor); + gradient.setColorAt(1,AEndColor); + painter.fillRect(ARect,gradient); + } else { + int Size = ARect.bottom() - ARect.top(); + QLinearGradient gradient(0,0,0,Size); + gradient.setColorAt(0,AStartColor); + gradient.setColorAt(1,AEndColor); + painter.fillRect(ARect,gradient); + } +} + +int MulDiv(int a, int b, int c) +{ + //todo: handle overflow? + return a*b/c; +} diff --git a/RedPandaIDE/qsynedit/MiscProcs.h b/RedPandaIDE/qsynedit/MiscProcs.h new file mode 100644 index 00000000..095fae87 --- /dev/null +++ b/RedPandaIDE/qsynedit/MiscProcs.h @@ -0,0 +1,90 @@ +#ifndef MISCPROCS_H +#define MISCPROCS_H +#include +#include +#include +#include +#include +#include "highlighter/base.h" +#include +#include +#include +#include +//#include +//#include + +class QPainter; +class QRect; +class QColor; + +using IntArray = QVector; +using PIntArray = std::shared_ptr; + +int MinMax(int x, int mi, int ma); +int MulDiv(int a, int b, int c); +void SwapInt(int& l, int &r); +QPoint MaxPoint(const QPoint& P1, const QPoint& P2); +QPoint MinPoint(const QPoint& P1, const QPoint& P2); + +PIntArray GetIntArray(size_t Count, int InitialValue); + +void InternalFillRect(QPainter* painter, const QRect& rcPaint, const QColor& color); + +// Converting tabs to spaces: To use the function several times it's better +// to use a function pointer that is set to the fastest conversion function. +using ConvertTabsProc = std::function; + +ConvertTabsProc GetBestConvertTabsProc(int TabWidth); + +// This is the slowest conversion function which can handle TabWidth <> 2^n. +QString ConvertTabs(const QString& Line, int TabWidth); + +using ConvertTabsProcEx = std::function; + +ConvertTabsProcEx GetBestConvertTabsProcEx(int TabWidth); +// This is the slowest conversion function which can handle TabWidth <> 2^n. +QString ConvertTabsEx(const QString& Line, int TabWidth, bool& HasTabs); + +bool GetHasTabs(const QString& line, int& CharsBefore); + +int GetExpandedLength(const QString& aStr, int aTabWidth); + +int CharIndex2CaretPos(int Index, int TabWidth, + const QString& Line); + +int CaretPos2CharIndex(int Position, int TabWidth, const QString& Line, + bool& InsideTabChar); + +// search for the first char of set AChars in Line, starting at index Start +int StrScanForCharInSet(const QString& Line, int Start, const QSet& AChars); +// the same, but searching backwards +int StrRScanForCharInSet(const QString& Line, int Start, const QSet& AChars); + +int GetEOL(const QString& Line, int start); + +// Remove all '/' characters from string by changing them into '\.'. +// Change all '\' characters into '\\' to allow for unique decoding. +QString EncodeString(const QString & s); + +// Decodes string, encoded with EncodeString. +QString DecodeString(const QString& s); + +using HighlighterAttriProc = std::function Params)>; + +// Enums all child highlighters and their attributes of a TSynMultiSyn through a +// callback function. +// This function also handles nested TSynMultiSyns including their MarkerAttri. +bool EnumHighlighterAttris(PSynHighlighter Highlighter, + bool SkipDuplicates, HighlighterAttriProc highlighterAttriProc, + std::initializer_list Params); + + +// Calculates Frame Check Sequence (FCS) 16-bit Checksum (as defined in RFC 1171) +uint16_t CalcFCS(unsigned char* ABuf, int ABufSize); + +void SynDrawGradient(QPaintDevice* ACanvas, const QColor& AStartColor, const QColor& AEndColor, + int ASteps, const QRect& ARect, bool AHorizontal); + +#endif // MISCPROCS_H diff --git a/RedPandaIDE/qsynedit/SynEdit.cpp b/RedPandaIDE/qsynedit/SynEdit.cpp new file mode 100644 index 00000000..a2c45c23 --- /dev/null +++ b/RedPandaIDE/qsynedit/SynEdit.cpp @@ -0,0 +1,1728 @@ +#include "SynEdit.h" +#include +#include +#include +#include +#include +#include +#include "highlighter/base.h" +#include "Constants.h" + +SynEdit::SynEdit(QWidget *parent) : QAbstractScrollArea(parent) +{ + mPaintLock = 0; + mPainterLock = 0; + mPainting = false; + mLines = std::make_shared(); + mOrigLines = mLines; + //fPlugins := TList.Create; + mMouseMoved = false; + mUndoing = false; + mLines->connect(mLines.get(), &SynEditStringList::changed, this, &SynEdit::linesChanged); + mLines->connect(mLines.get(), &SynEditStringList::changing, this, &SynEdit::linesChanging); + mLines->connect(mLines.get(), &SynEditStringList::cleared, this, &SynEdit::linesCleared); + mLines->connect(mLines.get(), &SynEditStringList::deleted, this, &SynEdit::linesDeleted); + mLines->connect(mLines.get(), &SynEditStringList::inserted, this, &SynEdit::linesInserted); + mLines->connect(mLines.get(), &SynEditStringList::putted, this, &SynEdit::linesPutted); +#ifdef Q_OS_WIN + mFontDummy = QFont("Consolas",10); +#elif Q_OS_LINUX + mFontDummy = QFont("terminal",14); +#else +#error "Not supported!" +#endif + mUndoList = std::make_shared(); + mUndoList->connect(mUndoList.get(), &SynEditUndoList::addedUndo, this, &SynEdit::undoAdded); + mOrigUndoList = mUndoList; + mRedoList = std::make_shared(); + mRedoList->connect(mRedoList.get(), &SynEditUndoList::addedUndo, this, &SynEdit::redoAdded); + mOrigRedoList = mRedoList; + //DoubleBuffered = false; + mActiveLineColor = QColor(); + mSelectedBackground = QColor(); + mSelectedForeground = QColor(); + + mBookMarkOpt.connect(&mBookMarkOpt, &SynBookMarkOpt::changed, this, &SynEdit::bookMarkOptionsChanged); + // fRightEdge has to be set before FontChanged is called for the first time + mRightEdge = 80; + mGutter.setRightOffset(21); + mGutter.connect(&mGutter, &SynGutter::changed, this, &SynEdit::gutterChanged); + mGutterWidth = mGutter.width(); + mTextOffset = mGutterWidth + 2; + //ControlStyle := ControlStyle + [csOpaque, csSetCaption, csNeedsBorderPaint]; + //Height := 150; + //Width := 200; + this->setCursor(Qt::CursorShape::IBeamCursor); + //TabStop := True; + mInserting = true; + mMaxScrollWidth = 1024; + mScrollBars = SynScrollStyle::ssBoth; + this->setFrameShape(QFrame::Panel); + this->setFrameShadow(QFrame::Sunken); + this->setLineWidth(1); + mInsertCaret = SynEditCaretType::ctVerticalLine; + mOverwriteCaret = SynEditCaretType::ctBlock; + mSelectionMode = SynSelectionMode::smNormal; + mActiveSelectionMode = SynSelectionMode::smNormal; + //stop qt to auto fill background + setAutoFillBackground(false); + //fFocusList := TList.Create; + //fKbdHandler := TSynEditKbdHandler.Create; + //fMarkList.OnChange := MarkListChange; + setDefaultKeystrokes(); + mRightEdgeColor = QColorConstants::Svg::silver; + /* IME input */ + mImeCount = 0; + mMBCSStepAside = false; + /* end of IME input */ + mWantReturns = true; + mWantTabs = false; + mTabWidth = 4; + mLeftChar = 1; + mTopLine = 1; + mCaretX = 1; + mLastCaretX = 1; + mCaretY = 1; + mBlockBegin.Char = 1; + mBlockBegin.Line = 1; + mBlockEnd = mBlockBegin; + mOptions = eoAutoIndent | eoDragDropEditing | eoEnhanceEndKey | + eoScrollPastEol | eoShowScrollHint | eoSmartTabs | eoTabsToSpaces | + eoSmartTabDelete| eoGroupUndo; + mScrollTimer = new QTimer(this); + mScrollTimer->setInterval(100); + connect(mScrollTimer, &QTimer::timeout,this, &SynEdit::scrollTimerHandler); + + mScrollHintColor = QColorConstants::Yellow; + mScrollHintFormat = SynScrollHintFormat::shfTopLineOnly; + + synFontChanged(); +} + +int SynEdit::displayLineCount() +{ + return lineToRow(mLines->count()); +} + +DisplayCoord SynEdit::displayXY() +{ + return bufferToDisplayPos(caretXY()); +} + +int SynEdit::displayX() +{ + return displayXY().Column; +} + +int SynEdit::displayY() +{ + return displayXY().Row; +} + +BufferCoord SynEdit::caretXY() +{ + BufferCoord result; + result.Char = caretX(); + result.Line = caretY(); + return result; +} + +int SynEdit::caretX() +{ + return mCaretX; +} + +int SynEdit::caretY() +{ + return mCaretY; +} + +void SynEdit::setCaretX(int value) +{ + setCaretXY({value,mCaretY}); +} + +void SynEdit::setCaretY(int value) +{ + setCaretXY({mCaretX,value}); +} + +void SynEdit::setCaretXY(const BufferCoord &value) +{ + setCaretXYCentered(false,value); +} + +void SynEdit::setCaretXYEx(bool CallEnsureCursorPos, BufferCoord value) +{ + bool vTriggerPaint=true; //how to test it? + + if (vTriggerPaint) + doOnPaintTransient(SynTransientType::ttBefore); + int nMaxX = mMaxScrollWidth + 1; + if (value.Line > mLines->count()) + value.Line = mLines->count(); + if (value.Line < 1) { + // this is just to make sure if Lines stringlist should be empty + value.Line = 1; + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) { + nMaxX = 1; + } + } else { + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) + nMaxX = mLines->getString(value.Line-1).length(); + } + if ((value.Char > nMaxX) && (! (mOptions.testFlag(SynEditorOption::eoScrollPastEol)) || + !(mOptions.testFlag(SynEditorOption::eoAutoSizeMaxScrollWidth))) ) + value.Char = nMaxX; + if (value.Char < 1) + value.Char = 1; + if ((value.Char != mCaretX) || (value.Line != mCaretY)) { + incPaintLock(); + auto action = finally([this]{ + decPaintLock(); + }); + // simply include the flags, fPaintLock is > 0 + if (mCaretX != value.Char) { + mCaretX = value.Char; + mStatusChanges.setFlag(SynStatusChange::scCaretX); + } + if (mCaretY != value.Line) { + if (!mActiveLineColor.isValid()) { + invalidateLine(value.Line); + invalidateLine(mCaretY); + } + mCaretY = value.Line; + mStatusChanges.setFlag(SynStatusChange::scCaretY); + } + // Call UpdateLastCaretX before DecPaintLock because the event handler it + // calls could raise an exception, and we don't want fLastCaretX to be + // left in an undefined state if that happens. + updateLastCaretX(); + if (CallEnsureCursorPos) + ensureCursorPosVisible(); + mStateFlags.setFlag(SynStateFlag::sfCaretChanged); + mStateFlags.setFlag(SynStateFlag::sfScrollbarChanged); + } else { + // Also call UpdateLastCaretX if the caret didn't move. Apps don't know + // anything about fLastCaretX and they shouldn't need to. So, to avoid any + // unwanted surprises, always update fLastCaretX whenever CaretXY is + // assigned to. + // Note to SynEdit developers: If this is undesirable in some obscure + // case, just save the value of fLastCaretX before assigning to CaretXY and + // restore it afterward as appropriate. + updateLastCaretX(); + } + if (vTriggerPaint) + doOnPaintTransient(SynTransientType::ttAfter); + +} + +void SynEdit::setCaretXYCentered(bool ForceToMiddle, const BufferCoord &value) +{ + incPaintLock(); + auto action = finally([this] { + decPaintLock(); + }); + mStatusChanges.setFlag(SynStatusChange::scSelection); + setCaretXYEx(ForceToMiddle,value); + if (selAvail()) + invalidateSelection(); + mBlockBegin.Char = mCaretX; + mBlockBegin.Line = mCaretY; + mBlockEnd = mBlockBegin; + if (ForceToMiddle) + ensureCursorPosVisibleEx(true); // but here after block has been set + +} + +void SynEdit::invalidateGutter() +{ + invalidateGutterLines(-1, -1); +} + +void SynEdit::invalidateGutterLine(int aLine) +{ + if ((aLine < 1) || (aLine > mLines->count())) + return; + + invalidateGutterLines(aLine, aLine); +} + +void SynEdit::invalidateGutterLines(int FirstLine, int LastLine) +{ + QRect rcInval; + if (!isVisible()) + return; + if (FirstLine == -1 && LastLine == -1) { + rcInval = QRect(clientLeft(), clientTop(), mGutterWidth, clientHeight()); + if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) + mInvalidateRect = mInvalidateRect.united(rcInval); + else + invalidateRect(rcInval); + } else { + // find the visible lines first + if (LastLine < FirstLine) + std::swap(LastLine, FirstLine); + if (mUseCodeFolding) { + FirstLine = lineToRow(FirstLine); + if (LastLine <= mLines->count()) + LastLine = lineToRow(LastLine); + else + LastLine = INT_MAX; + } + FirstLine = std::max(FirstLine, mTopLine); + LastLine = std::min(LastLine, mTopLine + mLinesInWindow); + // any line visible? + if (LastLine >= FirstLine) { + rcInval = {clientLeft(), clientTop()+mTextHeight * (FirstLine - mTopLine), + mGutterWidth, mTextHeight * (LastLine - mTopLine + 1)}; + if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) { + mInvalidateRect = mInvalidateRect.united(rcInval); + } else { + invalidateRect(rcInval); + } + } + } +} + +/** + * @brief Convert point on the edit (x,y) to (row,column) + * @param aX + * @param aY + * @return + */ +DisplayCoord SynEdit::pixelsToNearestRowColumn(int aX, int aY) +{ + // Result is in display coordinates + float f; + f = (aX - mGutterWidth - 2.0) / mCharWidth; + // don't return a partially visible last line + if (aY >= mLinesInWindow * mTextHeight) { + aY = mLinesInWindow * mTextHeight - 1; + if (aY < 0) + aY = 0; + } + return { + .Column = std::max(1, (int)(leftChar() + round(f))), + .Row = std::max(1, mTopLine + (aY / mTextHeight)) + }; +} + +DisplayCoord SynEdit::pixelsToRowColumn(int aX, int aY) +{ + return { + .Column = std::max(1, mLeftChar + ((aX - mGutterWidth - 2) / mCharWidth)), + .Row = std::max(1, mTopLine + (aY / mTextHeight)) + }; +} + +/** + * @brief takes a position in the text and transforms it into + * the row and column it appears to be on the screen + * @param p + * @return + */ +DisplayCoord SynEdit::bufferToDisplayPos(const BufferCoord &p) +{ + DisplayCoord result {p.Char,p.Line}; + // Account for tabs + if (p.Line-1 < mLines->count()) { + QString s = mLines->getString(p.Line - 1); + int l = s.length(); + int x = 0; + for (int i=0;icount() ) { + QString s = mLines->getString(Result.Line - 1); + int l = s.length(); + int x = 0; + int i = 0; + + while (x < p.Column) { + if (i < l && s[i] == '\t') + x += mTabWidth - (x % mTabWidth); + else + x += 1; + i++; + } + Result.Char = i; + } +} + +int SynEdit::rowToLine(int aRow) +{ + return displayToBufferPos({1, aRow}).Line; +} + +int SynEdit::lineToRow(int aLine) +{ + return bufferToDisplayPos({1, aLine}).Row; +} + +int SynEdit::foldRowToLine(int Row) +{ + int result = Row; + for (int i=0;icollapsed && !range->parentCollapsed() && range->fromLine < result) { + result += range->linesCollapsed; + } + } + return result; +} + +int SynEdit::foldLineToRow(int Line) +{ + int result = Line; + for (int i=mAllFoldRanges.count()-1;i>=0;i--) { + PSynEditFoldRange range =mAllFoldRanges[i]; + if (range->collapsed && !range->parentCollapsed()) { + // Line is found after fold + if (range->toLine < Line) + result -= range->linesCollapsed; + // Inside fold + else if (range->fromLine < Line && Line <= range->toLine) + result -= Line - range->fromLine; + } + } + return result; +} + +void SynEdit::setDefaultKeystrokes() +{ + mKeyStrokes.resetDefaults(); +} + +void SynEdit::invalidateLine(int Line) +{ + QRect rcInval; + if (mPainterLock >0) + return; + if (Line<1 || Line>mLines->count() || !isVisible()) + return; + + // invalidate text area of this line + if (mUseCodeFolding) + Line = foldLineToRow(Line); + if (Line >= mTopLine && Line <= mTopLine + mLinesInWindow) { + rcInval = { clientLeft() + mGutterWidth, + clientTop() + mTextHeight * (Line - mTopLine), + clientWidth(), + mTextHeight}; + if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) + mInvalidateRect = mInvalidateRect.united(rcInval); + else + invalidateRect(rcInval); + } +} + +void SynEdit::invalidateLines(int FirstLine, int LastLine) +{ + if (mPainterLock>0) + return; + + if (!isVisible()) + return; + if (FirstLine == -1 && LastLine == -1) { + QRect rcInval = clientRect(); + rcInval.setLeft(rcInval.left()+mGutterWidth); + if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) { + mInvalidateRect = mInvalidateRect.united(rcInval); + } else { + invalidateRect(rcInval); + } + } else { + FirstLine = std::max(FirstLine, 1); + LastLine = std::max(LastLine, 1); + // find the visible lines first + if (LastLine < FirstLine) + std::swap(LastLine, FirstLine); + + if (LastLine >= mLines->count()) + LastLine = INT_MAX; // paint empty space beyond last line + + if (mUseCodeFolding) { + FirstLine = lineToRow(FirstLine); + // Could avoid this conversion if (First = Last) and + // (Length < CharsInWindow) but the dependency isn't worth IMO. + if (LastLine < mLines->count()) + LastLine = lineToRow(LastLine + 1) - 1; + } + + // mTopLine is in display coordinates, so FirstLine and LastLine must be + // converted previously. + FirstLine = std::max(FirstLine, mTopLine); + LastLine = std::min(LastLine, mTopLine + mLinesInWindow); + + // any line visible? + if (LastLine >= FirstLine) { + QRect rcInval = { + clientLeft()+mGutterWidth, + mTextHeight * (FirstLine - mTopLine), + clientWidth(), mTextHeight * (LastLine - mTopLine + 1) + }; + if (mStateFlags.testFlag(SynStateFlag::sfLinesChanging)) + mInvalidateRect = mInvalidateRect.united(rcInval); + else + invalidateRect(rcInval); + } + } +} + +void SynEdit::invalidateSelection() +{ + if (mPainterLock>0) + return; + invalidateLines(mBlockBegin.Line, mBlockEnd.Line); +} + +void SynEdit::invalidateRect(const QRect &rect) +{ + if (mPainterLock>0) + return; + viewport()->update(rect); +} + +void SynEdit::invalidate() +{ + if (mPainterLock>0) + return; + viewport()->update(); +} + +void SynEdit::lockPainter() +{ + mPainterLock++; +} + +void SynEdit::unlockPainter() +{ + Q_ASSERT(mPainterLock>0); + mPainterLock--; +} + +bool SynEdit::selAvail() +{ + return (mBlockBegin.Char != mBlockEnd.Char) || + ((mBlockBegin.Line != mBlockEnd.Line) && (mActiveSelectionMode != SynSelectionMode::smColumn)); +} + +void SynEdit::setCaretAndSelection(const BufferCoord &ptCaret, const BufferCoord &ptBefore, const BufferCoord &ptAfter) +{ + SynSelectionMode vOldMode = mActiveSelectionMode; + incPaintLock(); + auto action = finally([this,vOldMode]{ + mActiveSelectionMode = vOldMode; + decPaintLock(); + }); + internalSetCaretXY(ptCaret); + setBlockBegin(ptBefore); + setBlockEnd(ptAfter); +} + +void SynEdit::clearUndo() +{ + mUndoList->Clear(); + mRedoList->Clear(); +} + +void SynEdit::clearAreaList(SynEditingAreaList areaList) +{ + areaList.clear(); +} + +void SynEdit::computeCaret(int X, int Y) +{ + DisplayCoord vCaretNearestPos = pixelsToNearestRowColumn(X, Y); + vCaretNearestPos.Row = MinMax(vCaretNearestPos.Row, 1, displayLineCount()); + setInternalDisplayXY(vCaretNearestPos); +} + +void SynEdit::computeScroll(int X, int Y) +{ + QRect iScrollBounds; // relative to the client area + // don't scroll if dragging text from other control +// if (not MouseCapture) and (not Dragging) then begin +// fScrollTimer.Enabled := False; +// Exit; +// end; + + iScrollBounds = QRect(mGutterWidth+this->frameWidth(), this->frameWidth(), mCharsInWindow * mCharWidth, + mLinesInWindow * mTextHeight); + + if (X < iScrollBounds.left()) + mScrollDeltaX = (X - iScrollBounds.left()) % mCharWidth - 1; + else if (X >= iScrollBounds.right()) + mScrollDeltaX = (X - iScrollBounds.right()) % mCharWidth + 1; + else + mScrollDeltaX = 0; + + if (Y < iScrollBounds.top()) + mScrollDeltaY = (Y - iScrollBounds.top()) % mTextHeight - 1; + else if (Y >= iScrollBounds.bottom()) + mScrollDeltaY = (Y - iScrollBounds.bottom()) % mTextHeight + 1; + else + mScrollDeltaY = 0; + + if (mScrollDeltaX!=0 || mScrollDeltaY!=0) + mScrollTimer->start(); +} + +void SynEdit::doBlockIndent() +{ + BufferCoord OrgCaretPos; + BufferCoord BB, BE; + QString StrToInsert; + int Run; + int e,x,i,InsertStrLen; + QString Spaces; + SynSelectionMode OrgSelectionMode; + BufferCoord InsertionPos; + + OrgSelectionMode = mActiveSelectionMode; + OrgCaretPos = caretXY(); + StrToInsert = nullptr; + if (selAvail()) { + auto action = finally([&,this]{ + if (BE.Char > 1) + BE.Char+=Spaces.length(); + setCaretAndSelection(OrgCaretPos, + {BB.Char + Spaces.length(), BB.Line}, BE); + setActiveSelectionMode(OrgSelectionMode); + }); + // keep current selection detail + BB = mBlockBegin; + BE = mBlockEnd; + // build text to insert + if (BE.Char == 1) { + e = BE.Line - 1; + x = 1; + } else { + e = BE.Line; + if (mOptions.testFlag(SynEditorOption::eoTabsToSpaces)) + x = caretX() + mTabWidth; + else + x = caretX() + 1; + } + if (mOptions.testFlag(eoTabsToSpaces)) { + InsertStrLen = (mTabWidth + 2) * (e - BB.Line) + mTabWidth + 1; + // chars per line * lines-1 + last line + null char + StrToInsert.resize(InsertStrLen); + Run = 0; + Spaces = QString(mTabWidth,' ') ; + } else { + InsertStrLen = 3 * (e - BB.Line) + 2; + // #9#13#10 * lines-1 + (last line's #9 + null char) + StrToInsert.resize(InsertStrLen); + Run = 0; + Spaces = "\t"; + } + for (i = BB.Line; iBeginBlock(); + auto action2=finally([this]{ + mUndoList->EndBlock(); + }); + InsertionPos.Line = BB.Line; + if (mActiveSelectionMode == SynSelectionMode::smColumn) + InsertionPos.Char = std::min(BB.Char, BE.Char); + else + InsertionPos.Char = 1; + insertBlock(InsertionPos, InsertionPos, StrToInsert, true); + mUndoList->AddChange(SynChangeReason::crIndent, BB, BE, "", SynSelectionMode::smColumn); + //We need to save the position of the end block for redo + mUndoList->AddChange(SynChangeReason::crIndent, + {BB.Char + Spaces.length(), BB.Line}, + {BE.Char + Spaces.length(), BE.Line}, + "", SynSelectionMode::smColumn); + //adjust the x position of orgcaretpos appropriately + OrgCaretPos.Char = x; + } + } + +} + +void SynEdit::incPaintLock() +{ + mPaintLock ++ ; +} + +void SynEdit::decPaintLock() +{ + Q_ASSERT(mPaintLock > 0); + mPaintLock--; + if (mPaintLock == 0 ) { + if (mStateFlags.testFlag(SynStateFlag::sfScrollbarChanged)) + updateScrollbars(); + if (mStateFlags.testFlag(SynStateFlag::sfCaretChanged)) + updateCaret(); + if (mStatusChanges!=0) + doOnStatusChange(mStatusChanges); + } +} + +bool SynEdit::mouseCapture() +{ + return hasMouseTracking(); +} + +int SynEdit::clientWidth() +{ + return viewport()->size().width(); +} + +int SynEdit::clientHeight() +{ + return viewport()->size().height(); +} + +int SynEdit::clientTop() +{ + return 0; +} + +int SynEdit::clientLeft() +{ + return 0; +} + +QRect SynEdit::clientRect() +{ + return QRect(clientLeft(),clientTop(), clientWidth(), clientHeight()); +} + +void SynEdit::synFontChanged() +{ + recalcCharExtent(); + sizeOrFontChanged(true); +} + +void SynEdit::doOnPaintTransient(SynTransientType TransientType) +{ + doOnPaintTransientEx(TransientType, false); +} + +void SynEdit::updateLastCaretX() +{ + mMBCSStepAside = false; + mLastCaretX = displayX(); +} + +void SynEdit::ensureCursorPosVisible() +{ + ensureCursorPosVisibleEx(false); +} + +void SynEdit::ensureCursorPosVisibleEx(bool ForceToMiddle) +{ + incPaintLock(); + auto action = finally([this]{ + decPaintLock(); + }); + // Make sure X is visible + int VisibleX = displayX(); + if (VisibleX < leftChar()) + setLeftChar(VisibleX); + else if (VisibleX >= mCharsInWindow + leftChar() && mCharsInWindow > 0) + setLeftChar(VisibleX - mCharsInWindow + 1); + else + setLeftChar(leftChar()); + // Make sure Y is visible + int vCaretRow = displayY(); + if (ForceToMiddle) { + if (vCaretRow < mTopLine || vCaretRow>(mTopLine + (mLinesInWindow - 1))) + setTopLine( vCaretRow - (mLinesInWindow - 1) / 2); + } else { + if (vCaretRow < mTopLine) + setTopLine(vCaretRow); + else if (vCaretRow > mTopLine + (mLinesInWindow - 1) && mLinesInWindow > 0) + setTopLine(vCaretRow - (mLinesInWindow - 1)); + else + setTopLine(mTopLine); + } +} + +void SynEdit::scrollWindow(int dx, int dy) +{ + int nx = horizontalScrollBar()->value()+dx; + int ny = verticalScrollBar()->value()+dy; + nx = std::min(std::max(horizontalScrollBar()->minimum(),nx),horizontalScrollBar()->maximum()); + ny = std::min(std::max(verticalScrollBar()->minimum(),ny),verticalScrollBar()->maximum()); + horizontalScrollBar()->setValue(nx); + verticalScrollBar()->setValue(ny); + +} + +void SynEdit::setInternalDisplayXY(const DisplayCoord &aPos) +{ + incPaintLock(); + internalSetCaretXY(displayToBufferPos(aPos)); + decPaintLock(); + updateLastCaretX(); +} + +void SynEdit::internalSetCaretXY(const BufferCoord &Value) +{ + setCaretXYEx(true, Value); +} + +void SynEdit::setStatusChanged(SynStatusChanges changes) +{ + mStatusChanges = mStatusChanges | changes; + if (mPaintLock == 0) + doOnStatusChange(mStatusChanges); +} + +void SynEdit::doOnStatusChange(SynStatusChanges) +{ + emit statusChanged(mStatusChanges); + mStatusChanges = SynStatusChange::scNone; +} + +void SynEdit::insertBlock(const BufferCoord &BB, const BufferCoord &BE, const QString &ChangeStr, bool AddToUndoList) +{ + setCaretAndSelection(BB, BB, BE); + setActiveSelectionMode(SynSelectionMode::smColumn); + setSelTextPrimitiveEx(SynSelectionMode::smColumn, ChangeStr, AddToUndoList); + setStatusChanged(SynStatusChange::scSelection); +} + +void SynEdit::updateScrollbars() +{ + int nMaxScroll; + int nMin,nMax,nPage,nPos; + if (mPaintLock!=0) { + mStateFlags.setFlag(SynStateFlag::sfScrollbarChanged); + } else { + mStateFlags.setFlag(SynStateFlag::sfScrollbarChanged,false); + if (mScrollBars != SynScrollStyle::ssNone) { + if (mOptions.testFlag(eoHideShowScrollbars)) { + setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); + setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); + } else { + setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); + setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); + } + if (mScrollBars == SynScrollStyle::ssBoth || mScrollBars == SynScrollStyle::ssHorizontal) { + if (mOptions.testFlag(eoScrollPastEol)) + nMaxScroll = mMaxScrollWidth; + else + nMaxScroll = std::max(mLines->lengthOfLongestLine(), 1); + if (nMaxScroll <= MAX_SCROLL) { + nMin = 1; + nMax = nMaxScroll; + nPage = mCharsInWindow; + nPos = mLeftChar; + } else { + nMin = 0; + nMax = MAX_SCROLL; + nPage = MulDiv(MAX_SCROLL, mCharsInWindow, nMaxScroll); + nPos = MulDiv(MAX_SCROLL, mLeftChar, nMaxScroll); + } + horizontalScrollBar()->setMinimum(nMin); + horizontalScrollBar()->setMaximum(nMax); + horizontalScrollBar()->setPageStep(nPage); + horizontalScrollBar()->setValue(nPos); + horizontalScrollBar()->setSingleStep(1); + } else + setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn); + + if (mScrollBars == SynScrollStyle::ssBoth || mScrollBars == SynScrollStyle::ssVertical) { + nMaxScroll = displayLineCount(); + if (mOptions.testFlag(eoScrollPastEof)) + nMaxScroll+=mLinesInWindow - 1; + if (nMaxScroll <= MAX_SCROLL) { + nMin = 1; + nMax = std::max(1, nMaxScroll); + nPage = mLinesInWindow; + nPos = mTopLine; + } else { + nMin = 0; + nMax = MAX_SCROLL; + nPage = MulDiv(MAX_SCROLL, mLinesInWindow, nMaxScroll); + nPos = MulDiv(MAX_SCROLL, mTopLine, nMaxScroll); + } + verticalScrollBar()->setMinimum(nMin); + verticalScrollBar()->setMaximum(nMax); + verticalScrollBar()->setPageStep(nPage); + verticalScrollBar()->setValue(nPos); + verticalScrollBar()->setSingleStep(1); + } else + setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); + } else { + setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); + setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); + } + } +} + +void SynEdit::updateCaret() +{ + mStateFlags.setFlag(SynStateFlag::sfCaretChanged,false); + //do nothing, because we will draw cursor in the paintEvent +} + +void SynEdit::recalcCharExtent() +{ + SynFontStyle styles[] = {SynFontStyle::fsBold, SynFontStyle::fsItalic, SynFontStyle::fsStrikeOut, SynFontStyle::fsUnderline}; + bool hasStyles[] = {false,false,false,false}; + int size = 4; + if (mHighlighter && mHighlighter->attributes().count()>0) { + for (PSynHighlighterAttribute attribute: mHighlighter->attributes().values()) { + for (int i=0;istyles().testFlag(styles[i])) + hasStyles[i] = true; + } + } + } else { + hasStyles[0] = font().bold(); + hasStyles[1] = font().italic(); + hasStyles[2] = font().strikeOut(); + hasStyles[3] = font().underline(); + } + + mTextHeight = 0; + mCharWidth = 0; + mTextHeight = fontMetrics().lineSpacing(); + mCharWidth = fontMetrics().horizontalAdvance("M"); + if (hasStyles[0]) { // has bold font + QFont f = font(); + f.setBold(true); + QFontMetrics fm(f); + if (fm.lineSpacing()>mTextHeight) + mTextHeight=fm.lineSpacing(); + if (fm.horizontalAdvance("M")>mCharWidth) + mCharWidth = fm.horizontalAdvance("M"); + } + if (hasStyles[1]) { // has strike out font + QFont f = font(); + f.setItalic(true); + QFontMetrics fm(f); + if (fm.lineSpacing()>mTextHeight) + mTextHeight=fm.lineSpacing(); + if (fm.horizontalAdvance("M")>mCharWidth) + mCharWidth = fm.horizontalAdvance("M"); + } + if (hasStyles[2]) { // has strikeout + QFont f = font(); + f.setStrikeOut(true); + QFontMetrics fm(f); + if (fm.lineSpacing()>mTextHeight) + mTextHeight=fm.lineSpacing(); + if (fm.horizontalAdvance("M")>mCharWidth) + mCharWidth = fm.horizontalAdvance("M"); + } + if (hasStyles[3]) { // has underline + QFont f = font(); + f.setUnderline(true); + QFontMetrics fm(f); + if (fm.lineSpacing()>mTextHeight) + mTextHeight=fm.lineSpacing(); + if (fm.horizontalAdvance("M")>mCharWidth) + mCharWidth = fm.horizontalAdvance("M"); + } + mTextHeight += mExtraLineSpacing; +} + +QString SynEdit::expandAtWideGlyphs(const QString &S) +{ + QString Result(S.length()*2); // speed improvement + int j = 0; + for (int i=0;i=Result.length()) + Result.resize(Result.length()+128); + // insert CountOfAvgGlyphs filling chars + while (CountOfAvgGlyphs>1) { + Result[j]=QChar(0xE000); + j++; + CountOfAvgGlyphs--; + } + Result[j]=S[i]; + j++; + } + Result.resize(j); +} + +void SynEdit::updateModifiedStatus() +{ + setModified(!mUndoList->initialState()); +} + +int SynEdit::scanFrom(int Index) +{ + SynRangeState iRange; + int Result = Index; + if (Result >= mLines->count()) + return Result; + + if (Result == 0) { + mHighlighter->resetState(); + } else { + mHighlighter->setState(mLines->ranges(Result-1), + mLines->braceLevels(Result-1), + mLines->bracketLevels(Result-1), + mLines->parenthesisLevels(Result-1)); + } + do { + mHighlighter->setLine(mLines->getString(Result), Result); + mHighlighter->nextToEol(); + iRange = mHighlighter->getRangeState(); + { + if (mLines->ranges(Result).state == iRange.state) + return Result;// avoid the final Decrement + } + mLines->setRange(Result,iRange); + mLines->setParenthesisLevel(Result,mHighlighter->getParenthesisLevel()); + mLines->setBraceLevel(Result,mHighlighter->getBraceLevel()); + mLines->setBracketLevel(Result,mHighlighter->getBracketLevel()); + Result ++ ; + } while (Result < mLines->count()); + Result--; + return Result; +} + +void SynEdit::uncollapse(PSynEditFoldRange FoldRange) +{ + FoldRange->linesCollapsed = 0; + FoldRange->collapsed = false; + + // Redraw the collapsed line + invalidateLines(FoldRange->fromLine, INT_MAX); + + // Redraw fold mark + invalidateGutterLines(FoldRange->fromLine, INT_MAX); +} + +void SynEdit::foldOnListInserted(int Line, int Count) +{ + // Delete collapsed inside selection + for (int i = mAllFoldRanges.count()-1;i>=0;i--) { + PSynEditFoldRange range = mAllFoldRanges[i]; + if (range->collapsed || range->parentCollapsed()){ + if (range->fromLine == Line - 1) // insertion starts at fold line + uncollapse(range); + else if (range->fromLine >= Line) // insertion of count lines above FromLine + range->move(Count); + } + } +} + +void SynEdit::foldOnListDeleted(int Line, int Count) +{ + // Delete collapsed inside selection + for (int i = mAllFoldRanges.count()-1;i>=0;i--) { + PSynEditFoldRange range = mAllFoldRanges[i]; + if (range->collapsed || range->parentCollapsed()){ + if (range->fromLine == Line && Count == 1) // open up because we are messing with the starting line + uncollapse(range); + else if (range->fromLine >= Line - 1 && range->fromLine < Line + Count) // delete inside affectec area + mAllFoldRanges.remove(i); + else if (range->fromLine >= Line + Count) // Move after affected area + range->move(-Count); + } + } + +} + +void SynEdit::foldOnListCleared() +{ + mAllFoldRanges.clear(); +} + +void SynEdit::rescan() +{ + if (!mUseCodeFolding) + return; + rescanForFoldRanges(); + invalidateGutter(); +} + +static void null_deleter(SynEditFoldRanges *) {} + +void SynEdit::rescanForFoldRanges() +{ + // Delete all uncollapsed folds + for (int i=mAllFoldRanges.count()-1;i>=0;i--) { + PSynEditFoldRange range =mAllFoldRanges[i]; + if (!range->collapsed && !range->parentCollapsed()) + mAllFoldRanges.remove(i); + } + + // Did we leave any collapsed folds and are we viewing a code file? + if (mAllFoldRanges.count() > 0) { + // Add folds to a separate list + PSynEditFoldRanges TemporaryAllFoldRanges = std::make_shared(); + scanForFoldRanges(TemporaryAllFoldRanges); + + // Combine new with old folds, preserve parent order + for (int i = 0; i< TemporaryAllFoldRanges->count();i++) { + for (int j = 0; j< mAllFoldRanges.count() - 1;j++) { + if (TemporaryAllFoldRanges->range(i)->fromLine < mAllFoldRanges[j]->fromLine) { + mAllFoldRanges.insert(j, TemporaryAllFoldRanges->range(i)); + break; + } + // If we can't prepend #i anywhere, just dump it at the end + if (j == mAllFoldRanges.count() - 1) + mAllFoldRanges.add(TemporaryAllFoldRanges->range(i)); + } + } + + } else { + // We ended up with no folds after deleting, just pass standard data... + PSynEditFoldRanges temp(&mAllFoldRanges, null_deleter); + scanForFoldRanges(temp); + } +} + +void SynEdit::scanForFoldRanges(PSynEditFoldRanges TopFoldRanges) +{ + // Recursively scan for folds (all types) + for (int i= 0 ; i< mCodeFolding.foldRegions.count() ; i++ ) { + findSubFoldRange(TopFoldRanges, i,PSynEditFoldRange()); + } +} + +//this func should only be used in findSubFoldRange +int SynEdit::lineHasChar(int Line, int startChar, QChar character, const QString& highlighterAttrName) { + QString CurLine = mLines->getString(Line); + if (!mHighlighter){ + for (int i=startChar; isetState(mLines->ranges(Line), + mLines->braceLevels(Line), + mLines->bracketLevels(Line), + mLines->parenthesisLevels(Line)); + mHighlighter->setLine(CurLine,Line); + */ + QString token; + while (!mHighlighter->eol()) { + token = mHighlighter->getToken(); + PSynHighlighterAttribute attr = mHighlighter->getTokenAttribute(); + if (token == character && attr->name()==highlighterAttrName) + return mHighlighter->getTokenPos(); + mHighlighter->next(); + } + } + return -1; +} + +void SynEdit::findSubFoldRange(PSynEditFoldRanges TopFoldRanges, int FoldIndex, PSynEditFoldRange Parent) +{ + PSynEditFoldRange CollapsedFold; + PSynEditFoldRanges ParentFoldRanges; + ParentFoldRanges = TopFoldRanges; + int Line = 0; + QString CurLine; + + if (!mHighlighter) + return; + while (Line < mLines->count()) { // index is valid for LinesToScan and fLines + // If there is a collapsed fold over here, skip it + CollapsedFold = collapsedFoldStartAtLine(Line + 1); // only collapsed folds remain + if (CollapsedFold) { + Line = CollapsedFold->toLine; + continue; + } + + // Find an opening character on this line + CurLine = mLines->getString(Line); + + mHighlighter->setState(mLines->ranges(Line), + mLines->braceLevels(Line), + mLines->bracketLevels(Line), + mLines->parenthesisLevels(Line)); + mHighlighter->setLine(CurLine,Line); + + QString token; + int pos; + while (!mHighlighter->eol()) { + token = mHighlighter->getToken(); + pos = mHighlighter->getTokenPos()+token.length(); + PSynHighlighterAttribute attr = mHighlighter->getTokenAttribute(); + // We've found a starting character and it have proper highlighting (ignore stuff inside comments...) + if (token == mCodeFolding.foldRegions.get(FoldIndex)->openSymbol && attr->name()==mCodeFolding.foldRegions.get(FoldIndex)->highlight) { + // And ignore lines with both opening and closing chars in them + if (lineHasChar(Line,pos,mCodeFolding.foldRegions.get(FoldIndex)->closeSymbol, + mCodeFolding.foldRegions.get(FoldIndex)->highlight)<0) { + // Add it to the top list of folds + Parent = ParentFoldRanges->addByParts( + Parent, + TopFoldRanges, + Line + 1, + mCodeFolding.foldRegions.get(FoldIndex), + Line + 1); + ParentFoldRanges = Parent->subFoldRanges; + + // Skip until a newline + break; + } + + } else if (token == mCodeFolding.foldRegions.get(FoldIndex)->closeSymbol && attr->name()==mCodeFolding.foldRegions.get(FoldIndex)->highlight) { + // And ignore lines with both opening and closing chars in them + if (lineHasChar(Line,pos,mCodeFolding.foldRegions.get(FoldIndex)->openSymbol, + mCodeFolding.foldRegions.get(FoldIndex)->highlight)<0) { + // Stop the recursion if we find a closing char, and return to our parent + if (Parent) { + Parent->toLine = Line + 1; + Parent = Parent->parent; + } + + // Skip until a newline + break; + } + } + mHighlighter->next(); + } + Line++; + } +} + +PSynEditFoldRange SynEdit::collapsedFoldStartAtLine(int Line) +{ + for (int i = 0; i< mAllFoldRanges.count() - 1; i++ ) { + if (mAllFoldRanges[i]->fromLine == Line && mAllFoldRanges[i]->collapsed) { + return mAllFoldRanges[i]; + } else if (mAllFoldRanges[i]->fromLine > Line) { + break; // sorted by line. don't bother scanning further + } + } + return PSynEditFoldRange(); +} + +void SynEdit::setSelTextPrimitiveEx(SynSelectionMode PasteMode, const QString &Value, bool AddToUndoList) +{ + //todo +} + +void SynEdit::doOnPaintTransientEx(SynTransientType TransientType, bool Lock) +{ + //todo: we can't draw to canvas outside paintEvent +} + +void SynEdit::initializeCaret() +{ + //todo: +} + +void SynEdit::sizeOrFontChanged(bool bFont) +{ + if (mCharWidth != 0) { + mCharsInWindow = std::max(clientWidth() - mGutterWidth - 2, 0) / mCharWidth; + mLinesInWindow = clientHeight() / mTextHeight; + if (bFont) { + if (mGutter.showLineNumbers()) + gutterChanged(); + else + updateScrollbars(); + initializeCaret(); + mStateFlags.setFlag(SynStateFlag::sfCaretChanged,false); + invalidate(); + } else + updateScrollbars(); + mStateFlags.setFlag(SynStateFlag::sfScrollbarChanged,false); + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) + setLeftChar(mLeftChar); + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEof)) + setTopLine(mTopLine); + } +} + +void SynEdit::doChange() +{ + emit Changed(); +} + +void SynEdit::paintEvent(QPaintEvent *event) +{ + if (mPainterLock>0) + return; + if (mPainting) + return; + mPainting = true; + QRect rcClip, rcDraw; + int nL1, nL2, nC1, nC2; + // Get the invalidated rect. Compute the invalid area in lines / columns. + rcClip = event->rect(); + // columns + nC1 = mLeftChar; + if (rcClip.left() > mGutterWidth + 2 + clientLeft()) + nC1 += (rcClip.left() - mGutterWidth - 2 - clientLeft()) % mCharWidth; + nC2 = mLeftChar + + (rcClip.right() - mGutterWidth - 2 - clientLeft() + mCharWidth - 1) % mCharWidth; + // lines + nL1 = MinMax(mTopLine + rcClip.top() % mTextHeight, mTopLine, displayLineCount()); + nL2 = MinMax(mTopLine + (rcClip.bottom() + mTextHeight - 1) % mTextHeight, 1, displayLineCount()); + + // Now paint everything while the caret is hidden. + hideCaret(); + auto action = finally([this] { + updateCaret(); + mPainting = false; + }); + + // First paint paint the text area if it was (partly) invalidated. + if (rcClip.right() > mGutterWidth + clientLeft()) { + rcDraw = rcClip; + rcDraw.setLeft( std::max(rcDraw.left(), mGutterWidth + clientLeft())); + paintTextLines(rcDraw, nL1, nL2, nC1, nC2); + } + + // Then the gutter area if it was (partly) invalidated. + if (rcClip.left() < clientLeft() + mGutterWidth) { + rcDraw = rcClip; + rcDraw.setRight(clientLeft() + mGutterWidth); + paintGutter(rcDraw, nL1, nL2); + } + + //PluginsAfterPaint(Canvas, rcClip, nL1, nL2); + // If there is a custom paint handler call it. + doOnPaint(); + doOnPaintTransient(SynTransientType:: ttAfter); +} + +int SynEdit::maxScrollWidth() const +{ + return mMaxScrollWidth; +} + +void SynEdit::setMaxScrollWidth(int Value) +{ + Value = MinMax(Value, 1, INT_MAX - 1); + if (mMaxScrollWidth != Value) { + mMaxScrollWidth = Value; + if (mOptions.testFlag(SynEditorOption::eoScrollPastEol)) + updateScrollbars(); + } +} + +bool SynEdit::modified() const +{ + return mModified; +} + +void SynEdit::setModified(bool Value) +{ + if (Value) + mLastModifyTime = QDateTime::currentDateTime(); + if (Value != mModified) { + mModified = Value; + if (mOptions.testFlag(SynEditorOption::eoGroupUndo) && (!Value) && mUndoList->CanUndo()) + mUndoList->AddGroupBreak(); + mUndoList->setInitialState(!Value); + statusChanged(SynStatusChange::scModified); + } +} + +int SynEdit::gutterWidth() const +{ + return mGutterWidth; +} + +void SynEdit::setGutterWidth(int Value) +{ + Value = std::max(Value, 0); + if (mGutterWidth != Value) { + mGutterWidth = Value; + mTextOffset = mGutterWidth + 2 - (mLeftChar - 1) * mCharWidth; + sizeOrFontChanged(false); + } +} + +int SynEdit::charWidth() const +{ + return mCharWidth; +} + +int SynEdit::charsInWindow() const +{ + return mCharsInWindow; +} + +void SynEdit::bookMarkOptionsChanged() +{ + invalidateGutter(); +} + +void SynEdit::linesChanged() +{ + SynSelectionMode vOldMode; + mStateFlags.setFlag(SynStateFlag::sfLinesChanging, false); + if (mUseCodeFolding) + rescan(); + + updateScrollbars(); + vOldMode = mActiveSelectionMode; + setBlockBegin(caretXY()); + mActiveSelectionMode = vOldMode; + invalidateRect(mInvalidateRect); + mInvalidateRect = {0,0,0,0}; + + if (mGutter.showLineNumbers() && (mGutter.autoSize())) + mGutter.autoSizeDigitCount(mLines->count()); + if (!mOptions.testFlag(SynEditorOption::eoScrollPastEof)) + setTopLine(mTopLine); +} + +void SynEdit::linesChanging() +{ + mStateFlags.setFlag(SynStateFlag::sfLinesChanging); +} + +void SynEdit::linesCleared() +{ + if (mUseCodeFolding) + foldOnListCleared(); + clearUndo(); + // invalidate the *whole* client area + mInvalidateRect={0,0,0,0}; + invalidate(); + // set caret and selected block to start of text + setCaretXY({1,1}); + // scroll to start of text + setTopLine(1); + setLeftChar(1); + mStatusChanges.setFlag(SynStatusChange::scAll); +} + +void SynEdit::linesDeleted(int index, int count) +{ + if (mUseCodeFolding) + foldOnListDeleted(index + 1, count); + if (mHighlighter && mLines->count() > 0) + scanFrom(index); + invalidateLines(index + 1, INT_MAX); + invalidateGutterLines(index + 1, INT_MAX); +} + +void SynEdit::linesInserted(int index, int count) +{ + if (mUseCodeFolding) + foldOnListInserted(index + 1, count); + if (mHighlighter && mLines->count() > 0) { + int vLastScan = index; + do { + vLastScan = scanFrom(vLastScan); + vLastScan++; + } while (vLastScan < index + count) ; + } + invalidateLines(index + 1, INT_MAX); + invalidateGutterLines(index + 1, INT_MAX); + if (mOptions.setFlag(SynEditorOption::eoAutoSizeMaxScrollWidth)) { + int L = mLines->expandedStringLength(index); + if (L > mMaxScrollWidth) + setMaxScrollWidth(L); + } +} + +void SynEdit::linesPutted(int index, int) +{ + int vEndLine = index + 1; + if (mHighlighter) { + vEndLine = std::max(vEndLine, scanFrom(index) + 1); + // If this editor is chained then the real owner of text buffer will probably + // have already parsed the changes, so ScanFrom will return immediately. + if (mLines != mOrigLines) + vEndLine = INT_MAX; + } + invalidateLines(index + 1, vEndLine); + + if (mOptions.setFlag(SynEditorOption::eoAutoSizeMaxScrollWidth)) { + int L = mLines->expandedStringLength(index); + if (L > mMaxScrollWidth) + setMaxScrollWidth(L); + } +} + +void SynEdit::undoAdded() +{ + updateModifiedStatus(); + + // we have to clear the redo information, since adding undo info removes + // the necessary context to undo earlier edit actions + if (! mUndoList->insideRedo() && + mUndoList->PeekItem() && (mUndoList->PeekItem()->changeReason()!=SynChangeReason::crGroupBreak)) + mRedoList->Clear(); + if (mUndoList->blockCount() == 0 ) + doChange(); +} + +SynSelectionMode SynEdit::activeSelectionMode() const +{ + return mActiveSelectionMode; +} + +void SynEdit::setActiveSelectionMode(const SynSelectionMode &Value) +{ + if (mActiveSelectionMode != Value) { + if (selAvail()) + invalidateSelection(); + mActiveSelectionMode = Value; + if (selAvail()) + invalidateSelection(); + setStatusChanged(SynStatusChange::scSelection); + } +} + +BufferCoord SynEdit::blockEnd() const +{ + return mBlockEnd; +} + +void SynEdit::setBlockEnd(BufferCoord Value) +{ + setActiveSelectionMode(mSelectionMode); + if (!mOptions.testFlag(eoNoSelection)) { + if (mOptions.testFlag(eoScrollPastEol)) + Value.Char = MinMax(Value.Char, 1, mMaxScrollWidth + 1); + else + Value.Char = std::max(Value.Char, 1); + Value.Line = MinMax(Value.Line, 1, mLines->count()); + if (mActiveSelectionMode == SynSelectionMode::smNormal) { + if (Value.Line >= 1 && Value.Line <= mLines->count()) + Value.Char = std::min(Value.Char, mLines->getString(Value.Line - 1).length() + 1); + else + Value.Char = 1; + } + if (Value.Char != mBlockEnd.Char || Value.Line != mBlockEnd.Line) { + if (Value.Char != mBlockEnd.Char || Value.Line != mBlockEnd.Line) { + if (mActiveSelectionMode == SynSelectionMode::smColumn && Value.Char != mBlockEnd.Char) { + invalidateLines( + std::min(mBlockBegin.Line, std::min(mBlockEnd.Line, Value.Line)), + std::max(mBlockBegin.Line, std::max(mBlockEnd.Line, Value.Line))); + mBlockEnd = Value; + } else { + int nLine = mBlockEnd.Line; + mBlockEnd = Value; + if (mActiveSelectionMode != SynSelectionMode::smColumn || mBlockBegin.Char != mBlockEnd.Char) + invalidateLines(nLine, mBlockEnd.Line); + } + setStatusChanged(SynStatusChange::scSelection); + } + } + } +} + +BufferCoord SynEdit::blockBegin() const +{ + return mBlockBegin; +} + +void SynEdit::setBlockBegin(BufferCoord value) +{ + int nInval1, nInval2; + bool SelChanged; + setActiveSelectionMode(mSelectionMode); + if (mOptions.testFlag(SynEditorOption::eoScrollPastEol)) + value.Char = MinMax(value.Char, 1, mMaxScrollWidth + 1); + else + value.Char = std::max(value.Char, 1); + value.Line = MinMax(value.Line, 1, mLines->count()); + if (mActiveSelectionMode == SynSelectionMode::smNormal) { + if (value.Line >= 1 && value.Line <= mLines->count()) + value.Char = std::min(value.Char, mLines->getString(value.Line - 1).length() + 1); + else + value.Char = 1; + } + if (selAvail()) { + if (mBlockBegin.Line < mBlockEnd.Line) { + nInval1 = std::min(value.Line, mBlockBegin.Line); + nInval2 = std::max(value.Line, mBlockEnd.Line); + } else { + nInval1 = std::min(value.Line, mBlockEnd.Line); + nInval2 = std::max(value.Line, mBlockBegin.Line); + }; + mBlockBegin = value; + mBlockEnd = value; + invalidateLines(nInval1, nInval2); + SelChanged = true; + } else { + SelChanged = + (mBlockBegin.Char != value.Char) || (mBlockBegin.Line != value.Line) || + (mBlockEnd.Char != value.Char) || (mBlockEnd.Line != value.Line); + mBlockBegin = value; + mBlockEnd = value; + } + if (SelChanged) + setStatusChanged(SynStatusChange::scSelection); +} + +int SynEdit::leftChar() const +{ + return mLeftChar; +} + +void SynEdit::setLeftChar(int Value) +{ + int MaxVal; + int iDelta; + //QRect iTextArea; + if (mOptions.testFlag(SynEditorOption::eoScrollPastEol)) { + if (mOptions.testFlag(SynEditorOption::eoAutoSizeMaxScrollWidth)) + MaxVal = INT_MAX - mCharsInWindow; + else + MaxVal = mMaxScrollWidth - mCharsInWindow + 1; + } else { + MaxVal = mLines->lengthOfLongestLine(); + if (MaxVal > mCharsInWindow) + MaxVal = MaxVal - mCharsInWindow + 1; + else + MaxVal = 1; + } + Value = MinMax(Value, 1, MaxVal); + if (Value != mLeftChar) { + iDelta = mLeftChar - Value; + mLeftChar = Value; + mTextOffset = mGutterWidth + 2 - (mLeftChar - 1) * mCharWidth; + if (std::abs(iDelta) < mCharsInWindow) { +// iTextArea = clientRect(); +// iTextArea.setLeft(iTextArea.left() + mGutterWidth + 2); + scrollWindow(iDelta * mCharWidth, 0); + } else { + invalidateLines(-1, -1); + } + if ( (mOptions & (SynEditorOption::eoAutoSizeMaxScrollWidth | SynEditorOption::eoScrollPastEol)) + && + (mMaxScrollWidth < mLeftChar + mCharsInWindow)) { + setMaxScrollWidth(mLeftChar + mCharsInWindow); + } else + updateScrollbars(); + setStatusChanged(SynStatusChange::scLeftChar); + } + +} + +int SynEdit::linesInWindow() const +{ + return mLinesInWindow; +} + +int SynEdit::topLine() const +{ + return mTopLine; +} + +void SynEdit::setTopLine(int Value) +{ + if (mOptions.testFlag(SynEditorOption::eoScrollPastEof)) + Value = std::min(Value, displayLineCount()); + else + Value = std::min(Value, displayLineCount() - mLinesInWindow + 1); + Value = std::max(Value, 1); + if (Value != mTopLine) { + int Delta = mTopLine - Value; + mTopLine = Value; + if (mPainterLock == 0) { + if (std::abs(Delta) < mLinesInWindow) { + scrollWindow(0, mTextHeight * Delta); + } else { + invalidate(); + } + } + updateScrollbars(); + setStatusChanged(SynStatusChange::scTopLine); + } +} + +void SynEdit::redoAdded() +{ + updateModifiedStatus(); + + if (mRedoList->blockCount() == 0 ) + doChange(); +} + +void SynEdit::gutterChanged() +{ + if (mGutter.showLineNumbers() && mGutter.autoSize()) + mGutter.autoSizeDigitCount(mLines->count()); + int nW; + if (mGutter.useFontStyle()) { + QFontMetrics fm=QFontMetrics(mGutter.font()); + nW = mGutter.realGutterWidth(fm.averageCharWidth()); + } else { + nW = mGutter.realGutterWidth(mCharWidth); + } + if (nW == mGutterWidth) + invalidateGutter(); + else + setGutterWidth(nW); +} + +void SynEdit::scrollTimerHandler() +{ + QPoint iMousePos; + DisplayCoord C; + int X, Y; + + iMousePos = QCursor::pos(); + iMousePos = mapFromGlobal(iMousePos); + C = pixelsToRowColumn(iMousePos.x(), iMousePos.y()); + C.Row = MinMax(C.Row, 1, displayLineCount()); + if (mScrollDeltaX != 0) { + setLeftChar(leftChar() + mScrollDeltaX); + X = leftChar(); + if (mScrollDeltaX > 0) // scrolling right? + X+=charsInWindow(); + C.Column = X; + } + if (mScrollDeltaY != 0) { + if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) + setTopLine(mTopLine + mScrollDeltaY * mLinesInWindow); + else + setTopLine(mTopLine + mScrollDeltaY); + Y = mTopLine; + if (mScrollDeltaY > 0) // scrolling down? + Y+=mLinesInWindow - 1; + C.Row = MinMax(Y, 1, displayLineCount()); + } + BufferCoord vCaret = displayToBufferPos(C); + if ((caretX() != vCaret.Char) || (caretY() != vCaret.Line)) { + // changes to line / column in one go + incPaintLock(); + auto action = finally([this]{ + decPaintLock(); + }); + internalSetCaretXY(vCaret); + + // if MouseCapture is True we're changing selection. otherwise we're dragging + if (mouseCapture()) + setBlockEnd(caretXY()); + } + computeScroll(iMousePos.x(), iMousePos.y()); +} diff --git a/RedPandaIDE/qsynedit/SynEdit.h b/RedPandaIDE/qsynedit/SynEdit.h new file mode 100644 index 00000000..b6be93f6 --- /dev/null +++ b/RedPandaIDE/qsynedit/SynEdit.h @@ -0,0 +1,426 @@ +#ifndef SYNEDIT_H +#define SYNEDIT_H + +#include +#include +#include +#include +#include +#include +#include +#include "MiscClasses.h" +#include "CodeFolding.h" +#include "Types.h" +#include "TextBuffer.h" +#include "KeyStrokes.h" + +enum class SynFontSmoothMethod { + None, AntiAlias, ClearType +}; + + +enum class SynScrollHintFormat { + shfTopLineOnly, shfTopToBottom +}; + +enum class SynScrollStyle { + ssNone, ssHorizontal, ssVertical, ssBoth +}; + +enum class SynEditCaretType { + ctVerticalLine, ctHorizontalLine, ctHalfBlock, ctBlock +}; + +enum class SynStatusChange { + scNone = 0, + scAll = 0x0001, + scCaretX = 0x0002, + scCaretY = 0x0004, + scLeftChar = 0x0008, + scTopLine = 0x0010, + scInsertMode = 0x0020, + scModified = 0x0040, + scSelection = 0x0080, + scReadOnly = 0x0100, + scOpenFile = 0x0200 +}; + +Q_DECLARE_FLAGS(SynStatusChanges, SynStatusChange) +Q_DECLARE_OPERATORS_FOR_FLAGS(SynStatusChanges) + +enum class SynStateFlag { + sfCaretChanged = 0x0001, + sfScrollbarChanged = 0x0002, + sfLinesChanging = 0x0004, + sfIgnoreNextChar = 0x0008, + sfCaretVisible = 0x0010, + sfDblClicked = 0x0020, + sfWaitForDragging = 0x0040 +}; + +Q_DECLARE_FLAGS(SynStateFlags,SynStateFlag) + +Q_DECLARE_OPERATORS_FOR_FLAGS(SynStateFlags) + +enum SynEditorOption { + eoAltSetsColumnMode = 0x00000001, //Holding down the Alt Key will put the selection mode into columnar format + eoAutoIndent = 0x00000002, //Will indent the caret on new lines with the same amount of leading white space as the preceding line + eoAddIndent = 0x00000004, //Will add one tab width of indent when typing { and :, and remove the same amount when typing } + eoAutoSizeMaxScrollWidth = 0x00000008, //Automatically resizes the MaxScrollWidth property when inserting text + //eoDisableScrollArrows = 0x00000010 , //Disables the scroll bar arrow buttons when you can't scroll in that direction any more + eoDragDropEditing = 0x00000020, //Allows you to select a block of text and drag it within the document to another location + eoDropFiles = 0x00000040, //Allows the editor accept OLE file drops + eoEnhanceHomeKey = 0x00000080, //enhances home key positioning, similar to visual studio + eoEnhanceEndKey = 0x00000100, //enhances End key positioning, similar to JDeveloper + eoGroupUndo = 0x00000200, //When undoing/redoing actions, handle all continous changes of the same kind in one call instead undoing/redoing each command separately + eoHalfPageScroll = 0x00000400, //When scrolling with page-up and page-down commands, only scroll a half page at a time + eoHideShowScrollbars = 0x00000800, //if enabled, then the scrollbars will only show when necessary. If you have ScrollPastEOL, then it the horizontal bar will always be there (it uses MaxLength instead) + eoKeepCaretX = 0x00001000 , //When moving through lines w/o Cursor Past EOL, keeps the X position of the cursor + eoNoCaret = 0x00002000, //Makes it so the caret is never visible + eoNoSelection = 0x00004000, //Disables selecting text + eoRightMouseMovesCursor = 0x00008000, //When clicking with the right mouse for a popup menu, move the cursor to that location + eoScrollByOneLess = 0x00010000, //Forces scrolling to be one less + eoScrollHintFollows = 0x00020000, //The scroll hint follows the mouse when scrolling vertically + eoScrollPastEof = 0x00040000, //Allows the cursor to go past the end of file marker + eoScrollPastEol = 0x00080000, //Allows the cursor to go past the last character into the white space at the end of a line + eoShowScrollHint = 0x00100000, //Shows a hint of the visible line numbers when scrolling vertically + eoShowSpecialChars = 0x00200000, //Shows the special Characters + eoSmartTabDelete = 0x00400000, //similar to Smart Tabs, but when you delete characters + eoSmartTabs = 0x00800000, //When tabbing, the cursor will go to the next non-white space character of the previous line + eoSpecialLineDefaultFg = 0x01000000, //disables the foreground text color override when using the OnSpecialLineColor event + eoTabIndent = 0x02000000, //When active and act as block indent, unindent when text is selected + eoTabsToSpaces = 0x04000000, //Converts a tab character to a specified number of space characters + eoShowRainbowColor = 0x08000000, + eoTrimTrailingSpaces = 0x10000000 //Spaces at the end of lines will be trimmed and not saved +}; + +Q_DECLARE_FLAGS(SynEditorOptions, SynEditorOption) + +Q_DECLARE_OPERATORS_FOR_FLAGS(SynEditorOptions) + +enum class SynReplaceAction { + raCancel, raSkip, raReplace, raReplaceAll +}; + +struct SynEditingArea { + int beginX; + int endX; + QColor color; +}; + + +using PSynEditingArea = std::shared_ptr; +using SynEditingAreaList = QList; +enum class SynEditingAreaType { + eatRectangleBorder, + eatWaveUnderLine, + eatUnderLine +}; + +enum class SynTransientType { + ttBefore, ttAfter +}; + +enum class SynScrollBarKind { + sbHorizontal, sbVertical +}; + +/* +using SynPaintTransientProc = std::function; + */ +using SynPlaceMarkProc = std::function; +using SynProcessCommandProc = std::function; +using SynMouseCursorProc = std::function; +using SynPaintProc = std::function; +using SynPreparePaintHighlightTokenProc = std::function; +using SynReplaceTextProc = std::function; +using SynSpecialLineColorsProc = std::function; +using SynEditingAreasProc = std::function; +using SynGutterGetTextProc = std::function; +using SynTGutterPaintProc = std::function; + +class SynEdit; +using PSynEdit = std::shared_ptr; + +class SynEdit : public QAbstractScrollArea +{ + Q_OBJECT +public: + explicit SynEdit(QWidget *parent = nullptr); + + int displayLineCount(); + DisplayCoord displayXY(); + int displayX(); + int displayY(); + BufferCoord caretXY(); + int caretX(); + int caretY(); + + void setCaretX(int value); + void setCaretY(int value); + void setCaretXY(const BufferCoord& value); + void setCaretXYEx(bool CallEnsureCursorPos, BufferCoord value); + void setCaretXYCentered(bool ForceToMiddle, const BufferCoord& value); + + void invalidateGutter(); + void invalidateGutterLine(int aLine); + void invalidateGutterLines(int FirstLine, int LastLine); + DisplayCoord pixelsToNearestRowColumn(int aX, int aY); + DisplayCoord pixelsToRowColumn(int aX, int aY); + DisplayCoord bufferToDisplayPos(const BufferCoord& p); + BufferCoord displayToBufferPos(const DisplayCoord& p); + int rowToLine(int aRow); + int lineToRow(int aLine); + int foldRowToLine(int Row); + int foldLineToRow(int Line); + void setDefaultKeystrokes(); + void invalidateLine(int Line); + void invalidateLines(int FirstLine, int LastLine); + void invalidateSelection(); + void invalidateRect(const QRect& rect); + void invalidate(); + void lockPainter(); + void unlockPainter(); + bool selAvail(); + void setCaretAndSelection(const BufferCoord& ptCaret, + const BufferCoord& ptBefore, + const BufferCoord& ptAfter); + void clearUndo(); + + int topLine() const; + void setTopLine(int value); + + int linesInWindow() const; + + int leftChar() const; + void setLeftChar(int Value); + + BufferCoord blockBegin() const; + void setBlockBegin(BufferCoord value); + + BufferCoord blockEnd() const; + void setBlockEnd(BufferCoord Value); + + SynSelectionMode activeSelectionMode() const; + void setActiveSelectionMode(const SynSelectionMode &Value); + + int charsInWindow() const; + + int charWidth() const; + + int gutterWidth() const; + void setGutterWidth(int value); + + bool modified() const; + void setModified(bool Value); + + int maxScrollWidth() const; + void setMaxScrollWidth(int Value); + +signals: + void Changed(); + + void ChainUndoAdded(); + void ChainRedoAdded(); + void ChainLinesChanging(); + void ChainLinesChanged(); + void ChainListCleared(); + + void ChainListDeleted(int Index, int Count); + void ChainListInserted(int Index, int Count); + void ChainListPutted(int Index, int Count); + + void FilesDropped(int X,int Y, const QStringList& AFiles); + void GutterClicked(Qt::MouseButton button, int x, int y, int line, PSynEditMark mark); + void ImeInputed(const QString& s); + + void contextHelp(const QString& word); + + void scrolled(SynScrollBarKind ScrollBar); + void statusChanged(SynStatusChanges changes); + +private: + void clearAreaList(SynEditingAreaList areaList); + void computeCaret(int X, int Y); + void computeScroll(int X, int Y); + void doBlockIndent(); + + void incPaintLock(); + void decPaintLock(); + bool mouseCapture(); + int clientWidth(); + int clientHeight(); + int clientTop(); + int clientLeft(); + QRect clientRect(); + void synFontChanged(); + void doOnPaintTransient(SynTransientType TransientType); + void updateLastCaretX(); + void ensureCursorPosVisible(); + void ensureCursorPosVisibleEx(bool ForceToMiddle); + void scrollWindow(int dx,int dy); + void setInternalDisplayXY(const DisplayCoord& aPos); + void internalSetCaretXY(const BufferCoord& Value); + void setStatusChanged(SynStatusChanges changes); + void doOnStatusChange(SynStatusChanges changes); + void insertBlock(const BufferCoord& BB, const BufferCoord& BE, const QString& ChangeStr, + bool AddToUndoList); + void updateScrollbars(); + void updateCaret(); + void recalcCharExtent(); + QString expandAtWideGlyphs(const QString& S); + void updateModifiedStatus(); + int scanFrom(int Index); + void uncollapse(PSynEditFoldRange FoldRange); + void foldOnListInserted(int Line, int Count); + void foldOnListDeleted(int Line, int Count); + void foldOnListCleared(); + void rescan(); // rescan for folds + void rescanForFoldRanges(); + void scanForFoldRanges(PSynEditFoldRanges TopFoldRanges); + int lineHasChar(int Line, int startChar, QChar character, const QString& highlighterAttrName); + void findSubFoldRange(PSynEditFoldRanges TopFoldRanges,int FoldIndex, PSynEditFoldRange Parent); + PSynEditFoldRange collapsedFoldStartAtLine(int Line); + void setSelTextPrimitiveEx(SynSelectionMode PasteMode, + const QString& Value, bool AddToUndoList); + void doOnPaintTransientEx(SynTransientType TransientType, bool Lock); + void initializeCaret(); + +private slots: + void bookMarkOptionsChanged(); + void gutterChanged(); + void linesChanged(); + void linesChanging(); + void linesCleared(); + void linesDeleted(int index, int count); + void linesInserted(int index, int count); + void linesPutted(int index, int count); + void redoAdded(); + void scrollTimerHandler(); + void undoAdded(); + void sizeOrFontChanged(bool bFont); + void doChange(); + +private: + SynEditFoldRanges mAllFoldRanges; + SynEditCodeFolding mCodeFolding; + bool mUseCodeFolding; + bool mAlwaysShowCaret; + BufferCoord mBlockBegin; + BufferCoord mBlockEnd; + int mCaretX; + int mLastCaretX; + int mCaretY; + int mCharsInWindow; + int mCharWidth; + QFont mFontDummy; + QColor mColor; + SynFontSmoothMethod mFontSmoothing; + bool mMouseMoved; + /* IME input */ + int mImeCount; + bool mMBCSStepAside; + /* end of IME input */ + bool mInserting; + bool mPainting; + PSynEditStringList mLines; + PSynEditStringList mOrigLines; + PSynEditUndoList mOrigUndoList; + PSynEditUndoList mOrigRedoList; + int mLinesInWindow; + int mLeftChar; + int mMaxScrollWidth; + int mPaintLock; // lock counter for internal calculations + bool mReadOnly; + int mRightEdge; + QColor mRightEdgeColor; + QColor mScrollHintColor; + SynScrollHintFormat mScrollHintFormat; + SynScrollStyle mScrollBars; + int mTextHeight; + int mTextOffset; + int mTopLine; + PSynHighlighter mHighlighter; + QColor mSelectedForeground; + QColor mSelectedBackground; + QColor mActiveLineColor; + PSynEditUndoList mUndoList; + PSynEditUndoList mRedoList; + SynEditMarkList mBookMarks; + int mMouseDownX; + int mMouseDownY; + SynBookMarkOpt mBookMarkOpt; + bool mHideSelection; + int mMouseWheelAccumulator; + SynEditCaretType mOverwriteCaret; + SynEditCaretType mInsertCaret; + QPoint mCaretOffset; + SynEditKeyStrokes mKeyStrokes; + bool mModified; + QDateTime mLastModifyTime; + SynEditMarkList mMarkList; + int mExtraLineSpacing; + SynSelectionMode mSelectionMode; + SynSelectionMode mActiveSelectionMode; //mode of the active selection + bool mWantReturns; + bool mWantTabs; + SynGutter mGutter; + int mTabWidth; + QRect mInvalidateRect; + SynStateFlags mStateFlags; + SynEditorOptions mOptions; + SynStatusChanges mStatusChanges; + int mLastKey; + Qt::KeyboardModifiers mLastKeyModifiers; + //fSearchEngine: TSynEditSearchCustom; + //fHookedCommandHandlers: TList; + //fKbdHandler: TSynEditKbdHandler; + // fFocusList: TList; + // fPlugins: TList; + QTimer* mScrollTimer; + int mScrollDeltaX; + int mScrollDeltaY; + + PSynEdit fChainedEditor; + + bool mShowSpecChar; + int mPaintTransientLock; + bool mIsScrolling; + int mPainterLock; // lock counter to prevent repaint while painting + bool mUndoing; + // event handlers + SynPlaceMarkProc mOnClearMark; + SynProcessCommandProc mOnCommandProcessed; + SynMouseCursorProc mOnMouseCursor; + SynPaintProc mOnPaint; + SynPreparePaintHighlightTokenProc mOnPaintHighlightToken; + SynPlaceMarkProc mOnPlaceMark; + SynProcessCommandProc mOnProcessingCommand; + SynProcessCommandProc mOnProcessingUserCommand; + + SynReplaceTextProc mOnReplaceText; + SynSpecialLineColorsProc mOnSpecialLineColors; + SynEditingAreasProc mOnEditingAreas; + SynGutterGetTextProc mOnGutterGetText; + SynTGutterPaintProc mOnGutterPaint; + int mGutterWidth; + + //caret blink related + int m_blinkTimerId; + int m_bliknStatus; + + + // QWidget interface +protected: + void paintEvent(QPaintEvent *event) override; + +friend class SynEditTextPainter; +}; + +#endif // SYNEDIT_H diff --git a/RedPandaIDE/qsynedit/TextBuffer.cpp b/RedPandaIDE/qsynedit/TextBuffer.cpp new file mode 100644 index 00000000..39423f1e --- /dev/null +++ b/RedPandaIDE/qsynedit/TextBuffer.cpp @@ -0,0 +1,906 @@ +#include "TextBuffer.h" +#include +#include +#include +#include +#include +#include "../utils.h" + +SynEditStringList::SynEditStringList(QObject* parent): + QObject(parent) +{ + mAppendNewLineAtEOF = true; + mFileEndingType = FileEndingType::Windows; + mIndexOfLongestLine = -1; + mUpdateCount = 0; + setTabWidth(8); +} + +static void ListIndexOutOfBounds(int index) { + throw IndexOutOfRange(index); +} + +int SynEditStringList::parenthesisLevels(int Index) +{ + if (Index>=0 && Index < mList.size()) { + return mList[Index]->fParenthesisLevel; + } else + return 0; +} + +int SynEditStringList::bracketLevels(int Index) +{ + if (Index>=0 && Index < mList.size()) { + return mList[Index]->fBracketLevel; + } else + return 0; +} + +int SynEditStringList::braceLevels(int Index) +{ + if (Index>=0 && Index < mList.size()) { + return mList[Index]->fBraceLevel; + } else + return 0; +} + +QString SynEditStringList::expandedStrings(int Index) +{ + if (Index>=0 && Index < mList.size()) { + if (mList[Index]->fFlags & SynEditStringFlag::sfHasNoTabs) + return mList[Index]->fString; + else + return ExpandString(Index); + } else + return QString(); +} + +int SynEditStringList::expandedStringLength(int Index) +{ + if (Index>=0 && Index < mList.size()) { + if (mList[Index]->fFlags & sfExpandedLengthUnknown) + return ExpandString(Index).length(); + else + return mList[Index]->fExpandedLength; + } else + return 0; +} + +int SynEditStringList::lengthOfLongestLine() +{ + if (mIndexOfLongestLine < 0) { + int MaxLen = -1; + mIndexOfLongestLine = -1; + if (mList.count() > 0 ) { + for (int i=0;i MaxLen) { + MaxLen = len; + mIndexOfLongestLine = i; + } + } + } + } + if (mIndexOfLongestLine >= 0) + return mList[mIndexOfLongestLine]->fExpandedLength; + else + return 0; +} + +SynRangeState SynEditStringList::ranges(int Index) +{ + if (Index>=0 && Index < mList.size()) { + return mList[Index]->fRange; + } else + return {0,0}; +} + +void SynEditStringList::InsertItem(int Index, const QString &s) +{ + beginUpdate(); + PSynEditStringRec line = std::make_shared(); + line->fString = s; + mIndexOfLongestLine = -1; + mList.insert(Index,line); + endUpdate(); +} + +ConvertTabsProcEx SynEditStringList::getConvertTabsProc() const +{ + return mConvertTabsProc; +} + +bool SynEditStringList::getAppendNewLineAtEOF() const +{ + return mAppendNewLineAtEOF; +} + +void SynEditStringList::setAppendNewLineAtEOF(bool appendNewLineAtEOF) +{ + mAppendNewLineAtEOF = appendNewLineAtEOF; +} + +void SynEditStringList::setRange(int Index, SynRangeState ARange) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + mList[Index]->fRange = ARange; + endUpdate(); +} + +void SynEditStringList::setParenthesisLevel(int Index, int level) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + mList[Index]->fParenthesisLevel = level; + endUpdate(); +} + +void SynEditStringList::setBracketLevel(int Index, int level) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + mList[Index]->fBracketLevel = level; + endUpdate(); +} + +void SynEditStringList::setBraceLevel(int Index, int level) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + mList[Index]->fBraceLevel = level; + endUpdate(); +} + +QString SynEditStringList::getString(int Index) +{ + if (Index<0 || Index>=mList.count()) { + return QString(); + } + return mList[Index]->fString; +} + +int SynEditStringList::count() +{ + return mList.count(); +} + +void *SynEditStringList::getObject(int Index) +{ + if (Index<0 || Index>=mList.count()) { + return nullptr; + } + return mList[Index]->fObject; +} + +QString SynEditStringList::text() +{ + return GetTextStr(); +} + +void SynEditStringList::setText(const QString &text) +{ + PutTextStr(text); +} + +void SynEditStringList::beginUpdate() +{ + if (mUpdateCount == 0) { + SetUpdateState(true); + } + mUpdateCount++; +} + +void SynEditStringList::endUpdate() +{ + mUpdateCount--; + if (mUpdateCount == 0) { + SetUpdateState(false); + } +} + +int SynEditStringList::tabWidth() +{ + return mTabWidth; +} + +void SynEditStringList::setTabWidth(int value) +{ + if (value != mTabWidth) { + mTabWidth = value; + mConvertTabsProc = GetBestConvertTabsProcEx(mTabWidth); + mIndexOfLongestLine = -1; + for (PSynEditStringRec& line:mList) { + line->fExpandedLength = -1; + line->fFlags = SynEditStringFlag::sfExpandedLengthUnknown; + } + } +} + +int SynEditStringList::add(const QString &s) +{ + beginUpdate(); + int Result = mList.count(); + InsertItem(Result, s); + emit inserted(Result,1); + endUpdate(); + return Result; +} + +int SynEditStringList::addStrings(const QStringList &Strings) +{ + if (Strings.count() > 0) { + mIndexOfLongestLine = -1; + beginUpdate(); + auto action = finally([this]{ + endUpdate(); + }); + int FirstAdded = mList.count(); + + for (const QString& s:Strings) { + add(s); + } + emit inserted(FirstAdded,Strings.count()); + } +} + +int SynEditStringList::getTextLength() +{ + int Result = 0; + for (const PSynEditStringRec& line: mList ) { + Result += line->fString.length(); + if (mFileEndingType == FileEndingType::Windows) { + Result += 2; + } else { + Result += 1; + } + } +} + +void SynEditStringList::clear() +{ + if (!mList.isEmpty()) { + beginUpdate(); + mIndexOfLongestLine = -1; + mList.clear(); + endUpdate(); + } +} + +void SynEditStringList::deleteLines(int Index, int NumLines) +{ + if (NumLines<=0) + return; + if ((Index < 0) || (Index >= mList.count())) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + auto action = finally([this]{ + endUpdate(); + }); + if (mIndexOfLongestLine>=Index && (mIndexOfLongestLine = mList.count())) { + ListIndexOutOfBounds(Index1); + } + if ((Index2 < 0) || (Index2 >= mList.count())) { + ListIndexOutOfBounds(Index2); + } + beginUpdate(); + mList.swapItemsAt(Index1,Index2); + if (mIndexOfLongestLine == Index1) { + mIndexOfLongestLine = Index2; + } else if (mIndexOfLongestLine == Index2) { + mIndexOfLongestLine = Index1; + } + endUpdate(); +} + +void SynEditStringList::Insert(int Index, const QString &s) +{ + if ((Index < 0) || (Index > mList.count())) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + InsertItem(Index, s); + emit inserted(Index,1); + endUpdate(); +} + +void SynEditStringList::deleteAt(int Index) +{ + if ((Index < 0) || (Index >= mList.count())) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + if (mIndexOfLongestLine == Index) + mIndexOfLongestLine = -1; + mList.removeAt(Index); + emit deleted(Index,1); + endUpdate(); +} + +QString SynEditStringList::GetTextStr() +{ + QString Result; + for (PSynEditStringRec& line:mList) { + Result.append(line->fString); + switch(mFileEndingType) { + case FileEndingType::Linux: + Result.append('\n'); + case FileEndingType::Windows: + Result.append("\r\n"); + case FileEndingType::Mac: + Result.append("\r"); + } + } + return Result; +} + +void SynEditStringList::putString(int Index, const QString &s) { + if (Index == mList.count()) { + add(s); + } else { + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + mIndexOfLongestLine = -1; + mList[Index]->fFlags = SynEditStringFlag::sfExpandedLengthUnknown; + mList[Index]->fString = s; + emit putted(Index,1); + endUpdate(); + } +} + +void SynEditStringList::putObject(int Index, void *AObject) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + beginUpdate(); + mList[Index]->fObject = AObject; + endUpdate(); +} + +void SynEditStringList::SetUpdateState(bool Updating) +{ + if (Updating) + emit changing(); + else + emit changed(); +} + +QString SynEditStringList::ExpandString(int Index) +{ + QString Result(""); + PSynEditStringRec line = mList[Index]; + if (line->fString.isEmpty()) { + line->fFlags = SynEditStringFlag::sfHasNoTabs; + line->fExpandedLength = 0; + } else { + bool hasTabs; + Result = mConvertTabsProc(line->fString,mTabWidth,hasTabs); + line->fExpandedLength = Result.length(); + if (hasTabs) { + line->fFlags = SynEditStringFlag::sfHasTabs; + } else { + line->fFlags = SynEditStringFlag::sfHasNoTabs; + } + } + return Result; +} + +void SynEditStringList::InsertLines(int Index, int NumLines) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + if (NumLines<=0) + return; + beginUpdate(); + auto action = finally([this]{ + endUpdate(); + }); + PSynEditStringRec line; + mList.insert(Index,NumLines,line); + for (int i=Index;i(); + mList[i]=line; + } + emit inserted(Index,NumLines); +} + +void SynEditStringList::InsertStrings(int Index, const QStringList &NewStrings) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + if (NewStrings.isEmpty()) + return; + beginUpdate(); + auto action = finally([this]{ + endUpdate(); + }); + PSynEditStringRec line; + mList.insert(Index,NewStrings.length(),line); + for (int i=0;i(); + line->fString = NewStrings[i]; + mList[i+Index]=line; + } + emit inserted(Index,NewStrings.length()); +} + +void SynEditStringList::InsertText(int Index, const QString &NewText) +{ + if (Index<0 || Index>=mList.count()) { + ListIndexOutOfBounds(Index); + } + if (NewText.isEmpty()) + return; + QStringList lines = TextToLines(NewText); + InsertStrings(Index,lines); +} + +void SynEditStringList::LoadFromFile(QFile &file, const QByteArray& encoding, QByteArray& realEncoding) +{ + if (!file.open(QFile::ReadOnly | QFile::Text)) + throw FileError(tr("Can't open file '%1' for read!").arg(file.fileName())); + if (!file.canReadLine()) + throw FileError(tr("Can't read from file '%1'!").arg(file.fileName())); + beginUpdate(); + auto action = finally([this]{ + endUpdate(); + }); + + //test for utf8 / utf 8 bom + if (encoding == ENCODING_AUTO_DETECT) { + QByteArray line = file.readLine(); + QTextCodec* codec; + QTextCodec::ConverterState * state; + bool needReread = false; + bool allAscii = true; + //test for BOM + if (line.isEmpty()) { + realEncoding = ENCODING_ASCII; + return; + } + if ((line.length()>=3) && ((unsigned char)line[0]==0xEF) && ((unsigned char)line[1]==0xBB) && ((unsigned char)line[2]==0xBF) ) { + realEncoding = ENCODING_UTF8_BOM; + line = line.mid(3); + codec = QTextCodec::codecForName(ENCODING_UTF8); + } else { + realEncoding = ENCODING_UTF8; + codec = QTextCodec::codecForName(ENCODING_UTF8); + } + clear(); + do { + if (allAscii) { + allAscii = isTextAllAscii(line); + } + if (allAscii) { + add(QString::fromLatin1(line)); + } else { + QString newLine = codec->toUnicode(line.constData(),line.length(),state); + if (state->invalidChars>0) { + needReread = true; + break; + } + add(newLine); + } + line = file.readLine(); + } while (!file.atEnd()); + if (!needReread) { + if (allAscii) + realEncoding = ENCODING_ASCII; + return; + } + realEncoding = ENCODING_SYSTEM_DEFAULT; + } else { + realEncoding = encoding; + } + + if (realEncoding == ENCODING_SYSTEM_DEFAULT) { + realEncoding = QTextCodec::codecForLocale()->name(); + } + file.reset(); + QTextStream textStream(&file); + if (realEncoding == ENCODING_UTF8_BOM) { + textStream.setAutoDetectUnicode(true); + textStream.setCodec(ENCODING_UTF8); + } else { + textStream.setAutoDetectUnicode(false); + textStream.setCodec(realEncoding); + } + QString line; + clear(); + while (textStream.readLineInto(&line)) { + add(line); + } +} + + + +void SynEditStringList::SaveToFile(QFile &file, const QByteArray& encoding, QByteArray& realEncoding) +{ + if (!file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) + throw FileError(tr("Can't open file '%1' for save!").arg(file.fileName())); + if (mList.isEmpty()) + return; + bool allAscii = true; + + QTextCodec* codec; + if (realEncoding == ENCODING_UTF8_BOM) { + codec = QTextCodec::codecForName(ENCODING_UTF8_BOM); + } else if (realEncoding == ENCODING_ASCII) { + codec = QTextCodec::codecForLocale(); + } + for (PSynEditStringRec& line:mList) { + if (allAscii) { + allAscii = isTextAllAscii(line->fString); + } + if (!allAscii) { + file.write(codec->fromUnicode(line->fString)); + } else { + file.write(line->fString.toLatin1()); + } + switch (mFileEndingType) { + case FileEndingType::Windows: + file.write("\r\n"); + break; + case FileEndingType::Linux: + file.write("\n"); + break; + case FileEndingType::Mac: + file.write("\r"); + break; + } + } + if (encoding == ENCODING_AUTO_DETECT && allAscii) { + realEncoding = ENCODING_ASCII; + } +} + + +void SynEditStringList::PutTextStr(const QString &text) +{ + beginUpdate(); + auto action = finally([this]{ + endUpdate(); + }); + clear(); + int pos = 0; + int start; + while (pos < text.length()) { + start = pos; + while (pos=text.length()) + break; + if (text[pos] == '\r') + pos++; + if (text[pos] == '\n') + pos++; + } +} + +FileEndingType SynEditStringList::getFileEndingType() const +{ + return mFileEndingType; +} + +void SynEditStringList::setFileEndingType(const FileEndingType &fileEndingType) +{ + mFileEndingType = fileEndingType; +} + +SynEditStringRec::SynEditStringRec(): + fString(), + fObject(nullptr), + fRange{0,0}, + fExpandedLength(-1), + fParenthesisLevel(0), + fBracketLevel(0), + fBraceLevel(0), + fFlags(SynEditStringFlag::sfExpandedLengthUnknown) +{ +} + + +SynEditUndoList::SynEditUndoList():QObject() +{ + mMaxUndoActions = 1024; + mNextChangeNumber = 1; + mInsideRedo = false; + + mBlockChangeNumber=0; + mBlockCount=0; + mFullUndoImposible=false; + mLockCount = 0; + mInitialChangeNumber = 0; +} + +void SynEditUndoList::AddChange(SynChangeReason AReason, const BufferCoord &AStart, + const BufferCoord &AEnd, const QString &ChangeText, + SynSelectionMode SelMode) +{ + if (mLockCount != 0) + return; + int changeNumber; + if (mBlockChangeNumber != 0) { + changeNumber = mBlockChangeNumber; + } else { + changeNumber = mNextChangeNumber; + if (mBlockCount == 0) { + mNextChangeNumber++; + if (mNextChangeNumber == 0) { + mNextChangeNumber++; + } + } + } + PSynEditUndoItem NewItem = std::make_shared(AReason, + SelMode,AStart,AEnd,ChangeText, + changeNumber); + PushItem(NewItem); +} + +void SynEditUndoList::AddGroupBreak() +{ + //Add the GroupBreak even if ItemCount = 0. Since items are stored in + //reverse order in TCustomSynEdit.fRedoList, a GroupBreak could be lost. + if (LastChangeReason() != SynChangeReason::crGroupBreak) { + AddChange(SynChangeReason::crGroupBreak, {0,0}, {0,0}, "", SynSelectionMode::smNormal); + } +} + +void SynEditUndoList::BeginBlock() +{ + mBlockCount++; + mBlockChangeNumber = mNextChangeNumber; +} + +void SynEditUndoList::Clear() +{ + mItems.clear(); + mFullUndoImposible = false; +} + +void SynEditUndoList::DeleteItem(int index) +{ + if (index <0 || index>=mItems.count()) { + ListIndexOutOfBounds(index); + } + mItems.removeAt(index); +} + +void SynEditUndoList::EndBlock() +{ + if (mBlockCount > 0) { + mBlockCount--; + if (mBlockCount == 0) { + int iBlockID = mBlockChangeNumber; + mBlockChangeNumber = 0; + mNextChangeNumber++; + if (mNextChangeNumber == 0) + mNextChangeNumber++; + if (mItems.count() > 0 && PeekItem()->changeNumber() == iBlockID) + emit addedUndo(); + } + } +} + +SynChangeReason SynEditUndoList::LastChangeReason() +{ + if (mItems.count() == 0) + return SynChangeReason::crNothing; + else + mItems.last()->changeReason(); +} + +void SynEditUndoList::Lock() +{ + mLockCount++; +} + +PSynEditUndoItem SynEditUndoList::PeekItem() +{ + if (mItems.count() == 0) + return PSynEditUndoItem(); + else + return mItems.last(); +} + +PSynEditUndoItem SynEditUndoList::PopItem() +{ + if (mItems.count() == 0) + return PSynEditUndoItem(); + else { + PSynEditUndoItem item = mItems.last(); + mItems.removeLast(); + return item; + } +} + +void SynEditUndoList::PushItem(PSynEditUndoItem Item) +{ + if (!Item) + return; + mItems.append(Item); + EnsureMaxEntries(); + if (Item->changeReason()!= SynChangeReason::crGroupBreak) + addedUndo(); +} + +void SynEditUndoList::Unlock() +{ + if (mLockCount > 0) + mLockCount--; +} + +bool SynEditUndoList::CanUndo() +{ + return mItems.count()>0; +} + +int SynEditUndoList::ItemCount() +{ + return mItems.count(); +} + +int SynEditUndoList::maxUndoActions() const +{ + return mMaxUndoActions; +} + +void SynEditUndoList::setMaxUndoActions(int maxUndoActions) +{ + mMaxUndoActions = maxUndoActions; +} + +bool SynEditUndoList::initialState() +{ + if (ItemCount() == 0) { + return mInitialChangeNumber == 0; + } else { + return PeekItem()->changeNumber() == mInitialChangeNumber; + } +} + +PSynEditUndoItem SynEditUndoList::item(int index) +{ + if (index <0 || index>=mItems.count()) { + ListIndexOutOfBounds(index); + } + return mItems[index]; +} + +void SynEditUndoList::setInitialState(const bool Value) +{ + if (Value) { + if (ItemCount() == 0) + mInitialChangeNumber = 0; + else + mInitialChangeNumber = PeekItem()->changeNumber(); + } else if (ItemCount() == 0) { + if (mInitialChangeNumber == 0) { + mInitialChangeNumber = -1; + } + } else if (PeekItem()->changeNumber() == mInitialChangeNumber) { + mInitialChangeNumber = -1; + } +} + +void SynEditUndoList::setItem(int index, PSynEditUndoItem Value) +{ + if (index <0 || index>=mItems.count()) { + ListIndexOutOfBounds(index); + } + mItems[index]=Value; +} + +int SynEditUndoList::blockChangeNumber() const +{ + return mBlockChangeNumber; +} + +void SynEditUndoList::setBlockChangeNumber(int blockChangeNumber) +{ + mBlockChangeNumber = blockChangeNumber; +} + +int SynEditUndoList::blockCount() const +{ + return mBlockCount; +} + +bool SynEditUndoList::insideRedo() const +{ + return mInsideRedo; +} + +void SynEditUndoList::setInsideRedo(bool insideRedo) +{ + mInsideRedo = insideRedo; +} + +bool SynEditUndoList::fullUndoImposible() const +{ + return mFullUndoImposible; +} + +void SynEditUndoList::EnsureMaxEntries() +{ + if (mItems.count() > mMaxUndoActions){ + mFullUndoImposible = true; + while (mItems.count() > mMaxUndoActions) { + mItems.removeFirst(); + } + } +} + +SynSelectionMode SynEditUndoItem::changeSelMode() const +{ + return mChangeSelMode; +} + +BufferCoord SynEditUndoItem::changeStartPos() const +{ + return mChangeStartPos; +} + +BufferCoord SynEditUndoItem::changeEndPos() const +{ + return mChangeEndPos; +} + +QString SynEditUndoItem::changeStr() const +{ + return mChangeStr; +} + +int SynEditUndoItem::changeNumber() const +{ + return mChangeNumber; +} + +SynEditUndoItem::SynEditUndoItem(SynChangeReason reason, SynSelectionMode selMode, + BufferCoord startPos, BufferCoord endPos, + const QString &str, int number) +{ + mChangeReason = reason; + mChangeSelMode = selMode; + mChangeStartPos = startPos; + mChangeEndPos = endPos; + mChangeStr = str; + mChangeNumber = number; +} + +SynChangeReason SynEditUndoItem::changeReason() const +{ + return mChangeReason; +} diff --git a/RedPandaIDE/qsynedit/TextBuffer.h b/RedPandaIDE/qsynedit/TextBuffer.h new file mode 100644 index 00000000..1542c0a2 --- /dev/null +++ b/RedPandaIDE/qsynedit/TextBuffer.h @@ -0,0 +1,228 @@ +#ifndef SYNEDITSTRINGLIST_H +#define SYNEDITSTRINGLIST_H + +#include +#include "highlighter/base.h" +#include +#include +#include "MiscProcs.h" +#include "../utils.h" +#include "Types.h" + +enum SynEditStringFlag { + sfHasTabs = 0x0001, + sfHasNoTabs = 0x0002, + sfExpandedLengthUnknown = 0x0004 +}; + +typedef int SynEditStringFlags; + +struct SynEditStringRec { + QString fString; + void * fObject; + SynRangeState fRange; + int fExpandedLength; + SynEditStringFlags fFlags; + int fParenthesisLevel; + int fBracketLevel; + int fBraceLevel; + +public: + explicit SynEditStringRec(); +}; + +typedef std::shared_ptr PSynEditStringRec; + +typedef QVector SynEditStringRecList; + +typedef std::shared_ptr PSynEditStringRecList; + +class SynEditStringList; + +typedef std::shared_ptr PSynEditStringList; + +using StringListChangeCallback = std::function; + +class QFile; +class SynEditStringList : public QObject +{ + Q_OBJECT +public: + explicit SynEditStringList(QObject* parent=nullptr); + + int parenthesisLevels(int Index); + int bracketLevels(int Index); + int braceLevels(int Index); + QString expandedStrings(int Index); + int expandedStringLength(int Index); + int lengthOfLongestLine(); + SynRangeState ranges(int Index); + void setRange(int Index, SynRangeState ARange); + void setParenthesisLevel(int Index, int level); + void setBracketLevel(int Index, int level); + void setBraceLevel(int Index, int level); + QString getString(int Index); + int count(); + void* getObject(int Index); + QString text(); + void setText(const QString& text); + + void putString(int Index, const QString& s); + void putObject(int Index, void * AObject); + + void beginUpdate(); + void endUpdate(); + + int tabWidth(); + void setTabWidth(int value); + int add(const QString& s); + int addStrings(const QStringList& Strings); + + int getTextLength(); + void clear(); + void deleteAt(int Index); + void deleteLines(int Index, int NumLines); + void Exchange(int Index1, int Index2); + void Insert(int Index, const QString& s); + void InsertLines(int Index, int NumLines); + void InsertStrings(int Index, const QStringList& NewStrings); + void InsertText(int Index,const QString& NewText); + void LoadFromFile(QFile& file, const QByteArray& encoding, QByteArray& realEncoding); + void SaveToFile(QFile& file, const QByteArray& encoding, QByteArray& realEncoding); + + bool getAppendNewLineAtEOF() const; + void setAppendNewLineAtEOF(bool appendNewLineAtEOF); + + ConvertTabsProcEx getConvertTabsProc() const; + + FileEndingType getFileEndingType() const; + void setFileEndingType(const FileEndingType &fileEndingType); + +signals: + void changed(); + void changing(); + void cleared(); + void deleted(int index, int count); + void inserted(int index, int count); + void putted(int index, int count); +protected: + QString GetTextStr(); + void SetUpdateState(bool Updating); + void InsertItem(int Index, const QString& s); + void PutTextStr(const QString& text); + +private: + SynEditStringRecList mList; + + //int mCount; + //int mCapacity; + FileEndingType mFileEndingType; + bool mAppendNewLineAtEOF; + ConvertTabsProcEx mConvertTabsProc; + int mIndexOfLongestLine; + int mTabWidth; + int mUpdateCount; + + QString ExpandString(int Index); +}; + +enum class SynChangeReason {crInsert, crPaste, crDragDropInsert, + //several undo entries can be chained together via the ChangeNumber + //see also TCustomSynEdit.[Begin|End]UndoBlock methods + crDeleteAfterCursor, crDelete, + crLineBreak, crIndent, crUnindent, + crSilentDelete, crSilentDeleteAfterCursor, + crAutoCompleteBegin, crAutoCompleteEnd, + crPasteBegin, crPasteEnd, //for pasting, since it might do a lot of operations + crSpecial1Begin, crSpecial1End, + crSpecial2Begin, crSpecial2End, + crCaret, //just restore the Caret, allowing better Undo behavior + crSelection, //restore Selection + crNothing, + crGroupBreak, + crDeleteAll + }; +class SynEditUndoItem { +private: + SynChangeReason mChangeReason; + SynSelectionMode mChangeSelMode; + BufferCoord mChangeStartPos; + BufferCoord mChangeEndPos; + QString mChangeStr; + int mChangeNumber; +public: + SynEditUndoItem(SynChangeReason reason, + SynSelectionMode selMode, + BufferCoord startPos, + BufferCoord endPos, + const QString& str, + int number); + + SynChangeReason changeReason() const; + SynSelectionMode changeSelMode() const; + BufferCoord changeStartPos() const; + BufferCoord changeEndPos() const; + QString changeStr() const; + int changeNumber() const; +}; +using PSynEditUndoItem = std::shared_ptr; + +class SynEditUndoList : public QObject { + Q_OBJECT +public: + explicit SynEditUndoList(); + + void AddChange(SynChangeReason AReason, const BufferCoord& AStart, const BufferCoord& AEnd, + const QString& ChangeText, SynSelectionMode SelMode); + + void AddGroupBreak(); + void BeginBlock(); + void Clear(); + void DeleteItem(int index); + void EndBlock(); + SynChangeReason LastChangeReason(); + void Lock(); + PSynEditUndoItem PeekItem(); + PSynEditUndoItem PopItem(); + void PushItem(PSynEditUndoItem Item); + void Unlock(); + + bool CanUndo(); + int ItemCount(); + + int maxUndoActions() const; + void setMaxUndoActions(int maxUndoActions); + bool initialState(); + PSynEditUndoItem item(int index); + void setInitialState(const bool Value); + void setItem(int index, PSynEditUndoItem Value); + + int blockChangeNumber() const; + void setBlockChangeNumber(int blockChangeNumber); + + int blockCount() const; + + bool insideRedo() const; + void setInsideRedo(bool insideRedo); + + bool fullUndoImposible() const; + +signals: + void addedUndo(); + +protected: + int mBlockChangeNumber; + int mBlockCount; + bool mFullUndoImposible; + QVector mItems; + int mLockCount; + int mMaxUndoActions; + int mNextChangeNumber; + int mInitialChangeNumber; + bool mInsideRedo; + void EnsureMaxEntries(); +}; + +using PSynEditUndoList = std::shared_ptr; + +#endif // SYNEDITSTRINGLIST_H diff --git a/RedPandaIDE/qsynedit/TextPainter.cpp b/RedPandaIDE/qsynedit/TextPainter.cpp new file mode 100644 index 00000000..a1dff7f2 --- /dev/null +++ b/RedPandaIDE/qsynedit/TextPainter.cpp @@ -0,0 +1,130 @@ +#include "TextPainter.h" +#include "SynEdit.h" +#include "Constants.h" +#include + +SynEditTextPainter::SynEditTextPainter(SynEdit *edit) +{ + this->edit = edit; +} + +QColor SynEditTextPainter::colEditorBG() +{ + if (edit->mActiveLineColor.isValid() && bCurrentLine) { + return edit->mActiveLineColor; + } else { + QColor result = edit->mColor; + if (edit->mHighlighter) { + PSynHighlighterAttribute attr = edit->mHighlighter->whitespaceAttribute(); + if (attr && attr->background().isValid()) { + return attr->background(); + } + } + } + return edit->mColor; +} + +void SynEditTextPainter::ComputeSelectionInfo() +{ + BufferCoord vStart; + BufferCoord vEnd; + bAnySelection = false; + // Only if selection is visible anyway. + if (!edit->mHideSelection || edit->hasFocus()) { + bAnySelection = true; + // Get the *real* start of the selected area. + if (edit->mBlockBegin.Line < edit->mBlockEnd.Line) { + vStart = edit->mBlockBegin; + vEnd = edit->mBlockEnd; + } else if (edit->mBlockBegin.Line > edit->mBlockEnd.Line) { + vEnd = edit->mBlockBegin; + vStart = edit->mBlockEnd; + } else if (edit->mBlockBegin.Char != edit->mBlockEnd.Char) { + // No selection at all, or it is only on this line. + vStart.Line = edit->mBlockBegin.Line; + vEnd.Line = vStart.Line; + if (edit->mBlockBegin.Char < edit->mBlockEnd.Char) { + vStart.Char = edit->mBlockBegin.Char; + vEnd.Char = edit->mBlockEnd.Char; + } else { + vStart.Char = edit->mBlockEnd.Char; + vEnd.Char = edit->mBlockBegin.Char; + } + } else + bAnySelection = false; + // If there is any visible selection so far, then test if there is an + // intersection with the area to be painted. + if (bAnySelection) { + // Don't care if the selection is not visible. + bAnySelection = (vEnd.Line >= vFirstLine) and (vStart.Line <= vLastLine); + if (bAnySelection) { + // Transform the selection from text space into screen space + vSelStart = edit->bufferToDisplayPos(vStart); + vSelEnd = edit->bufferToDisplayPos(vEnd); + // In the column selection mode sort the begin and end of the selection, + // this makes the painting code simpler. + if (edit->mActiveSelectionMode == SynSelectionMode::smColumn && vSelStart.Column > vSelEnd.Column) + std::swap(vSelStart.Column, vSelEnd.Column); + } + } + } +} + +void SynEditTextPainter::setDrawingColors(bool Selected) +{ + if (Selected) { + painter->setPen(colSelFG); + painter->setBrush(colSelBG); + painter->setBackground(colSelBG); + } else { + painter->setPen(colFG); + painter->setBrush(colBG); + painter->setBackground(colBG); + } +} + +int SynEditTextPainter::ColumnToXValue(int Col) +{ + return edit->mTextOffset + (Col - 1) * edit->mCharWidth; +} + +void SynEditTextPainter::PaintToken(const QString &Token, int TokenLen, int CharsBefore, int First, int Last, bool isSelection) +{ + bool startPaint; + int nX; + + if (Last >= First && rcToken.right() > rcToken.left()) { + nX = ColumnToXValue(First); + First -= CharsBefore; + Last -= CharsBefore; + if (First > TokenLen) { + } else { + painter->setClipRect(rcToken); + int tokenColLen=0; + startPaint = false; + for (int i=0;imTabWidth - ((CharsBefore+tokenColLen) % edit->mTabWidth); + } else { + charCols = std::ceil(edit->fontMetrics().horizontalAdvance(Token[i]) * edit->fontMetrics().fontDpi() / 96.0 / edit->mCharWidth); + } + if (tokenColLen+charCols>=First) { + startPaint = true; + if (tokenColLen+1!=First) { + nX-= (First - tokenColLen - 1) * edit->mCharWidth; + } + } + painter->drawText(nX,rcToken.bottom(), Token[i]); + + tokenColLen += charCols; + nX += charCols * edit->mCharWidth; + if (tokenColLen > Last) + break; + } + } + + rcToken.setLeft(rcToken.right()); + } +} + diff --git a/RedPandaIDE/qsynedit/TextPainter.h b/RedPandaIDE/qsynedit/TextPainter.h new file mode 100644 index 00000000..17ea7d12 --- /dev/null +++ b/RedPandaIDE/qsynedit/TextPainter.h @@ -0,0 +1,52 @@ +#ifndef TEXTPAINTER_H +#define TEXTPAINTER_H + +#include +#include +#include +#include "Types.h" + +class SynEdit; +class SynEditTextPainter +{ + struct TokenAccu { + int Len; + int MaxLen; + int CharsBefore; + QString s; + QColor FG; + QColor BG; + SynFontStyles Style; + }; + +public: + SynEditTextPainter(SynEdit * edit); + + QColor colEditorBG(); + void ComputeSelectionInfo(); + void setDrawingColors(bool Selected); + int ColumnToXValue(int Col); + void PaintToken(const QString& Token, int TokenLen, int CharsBefore, + int First, int Last, bool isSelection); +private: + SynEdit* edit; + QPainter* painter; + bool bDoRightEdge; // right edge + int nRightEdge; + // selection info + bool bAnySelection; // any selection visible? + DisplayCoord vSelStart; // start of selected area + DisplayCoord vSelEnd; // end of selected area + // info about normal and selected text and background colors + bool bSpecialLine, bLineSelected, bCurrentLine; + QColor colFG, colBG; + QColor colSelFG, colSelBG; + // info about selection of the current line + int nLineSelStart, nLineSelEnd; + bool bComplexLine; + // painting the background and the text + QRect rcLine, rcToken; + int vFirstLine, vLastLine; +}; + +#endif // TEXTPAINTER_H diff --git a/RedPandaIDE/qsynedit/Types.h b/RedPandaIDE/qsynedit/Types.h new file mode 100644 index 00000000..ee49bad7 --- /dev/null +++ b/RedPandaIDE/qsynedit/Types.h @@ -0,0 +1,36 @@ +#ifndef TYPES_H +#define TYPES_H + +#include +#include +#include + +enum class SynSelectionMode {smNormal, smLine, smColumn}; + +struct BufferCoord { + int Char; + int Line; +}; + +struct DisplayCoord { + int Column; + int Row; +}; + +enum SynFontStyle { + fsNone = 0, + fsBold = 0x0001, + fsItalic = 0x0002, + fsUnderline = 0x0004, + fsStrikeOut = 0x0008 +}; + +Q_DECLARE_FLAGS(SynFontStyles,SynFontStyle) + +Q_DECLARE_OPERATORS_FOR_FLAGS(SynFontStyles) + +using PSynIcon = std::shared_ptr; +using SynIconList = QList; +using PSynIconList = std::shared_ptr; + +#endif // TYPES_H diff --git a/RedPandaIDE/qsynedit/highlighter/base.cpp b/RedPandaIDE/qsynedit/highlighter/base.cpp new file mode 100644 index 00000000..a09200c9 --- /dev/null +++ b/RedPandaIDE/qsynedit/highlighter/base.cpp @@ -0,0 +1,228 @@ +#include "base.h" +#include "../Constants.h" + +SynHighlighter::SynHighlighter(QObject *parent) : QObject(parent), + mWordBreakChars{ SynWordBreakChars }, + mEnabled(true), + mUpdateCount(0) +{ + +} + +const QMap& SynHighlighter::attributes() const +{ + return mAttributes; +} + +const QSet& SynHighlighter::wordBreakChars() const +{ + return mWordBreakChars; +} + +PSynHighlighterAttribute SynHighlighter::commentAttribute() const +{ + return mCommentAttribute; +} + +PSynHighlighterAttribute SynHighlighter::identifierAttribute() const +{ + return mIdentifierAttribute; +} + +PSynHighlighterAttribute SynHighlighter::keywordAttribute() const +{ + return mKeywordAttribute; +} + +PSynHighlighterAttribute SynHighlighter::stringAttribute() const +{ + return mStringAttribute; +} + +PSynHighlighterAttribute SynHighlighter::whitespaceAttribute() const +{ + return mWhitespaceAttribute; +} + +PSynHighlighterAttribute SynHighlighter::symbolAttribute() const +{ + return mSymbolAttribute; +} + +void SynHighlighter::onAttributeChanged() +{ + setAttributesChanged(); +} + +void SynHighlighter::setAttributesChanged() +{ + if (mUpdateCount == 0) { + emit attributesChanged(); + } +} + +void SynHighlighter::beginUpdate() +{ + mUpdateCount++; +} + +void SynHighlighter::endUpdate() +{ + mUpdateCount--; + if (mUpdateCount == 0) { + setAttributesChanged(); + } + if (mUpdateCount<0) { + throw new std::out_of_range("mUpdateCount in SynHighlighterBase < 0"); + } +} + +SynRangeState SynHighlighter::getRangeState() const +{ + return {0,0}; +} + +int SynHighlighter::getBraceLevel() const +{ + return 0; +} + +int SynHighlighter::getBracketLevel() const +{ + return 0; +} + +int SynHighlighter::getParenthesisLevel() const +{ + return 0; +} + +SynHighlighterTokenType SynHighlighter::getTokenType() +{ + return SynHighlighterTokenType::Default; +} + +bool SynHighlighter::isKeyword(const QString &) +{ + return false; +} + +void SynHighlighter::nextToEol() +{ + while (!eol()) + next(); +} + +bool SynHighlighter::isSpaceChar(const QChar &ch) +{ + return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; +} + +bool SynHighlighter::isIdentChar(const QChar &ch) const +{ + if (ch == '_') { + return true; + } + if (ch>='0' && ch <= '9') { + return true; + } + if (ch>='a' && ch <= 'z') { + return true; + } + if (ch>='A' && ch <= 'A') { + return true; + } + +} + +void SynHighlighter::addAttribute(PSynHighlighterAttribute attribute) +{ + mAttributes[attribute->name()]=attribute; + connect(attribute.get(), &SynHighlighterAttribute::changed, + this, &SynHighlighter::setAttributesChanged); +} + +void SynHighlighter::clearAttributes() +{ + mAttributes.clear(); +} + +int SynHighlighter::attributesCount() const +{ + return mAttributes.size(); +} + +PSynHighlighterAttribute SynHighlighter::getAttribute(const QString &name) const +{ + auto search = mAttributes.find(name); + if (search!=mAttributes.end()) { + return search.value(); + } + return PSynHighlighterAttribute(); +} + +void SynHighlighterAttribute::setChanged() +{ + emit changed(); +} + +SynFontStyles SynHighlighterAttribute::styles() const +{ + return mStyles; +} + +void SynHighlighterAttribute::setStyles(const SynFontStyles &styles) +{ + if (mStyles!=styles) { + mStyles = styles; + setChanged(); + } +} + +QString SynHighlighterAttribute::name() const +{ + return mName; +} + +void SynHighlighterAttribute::setName(const QString &name) +{ + if (mName!=name) { + mName = name; + setChanged(); + } +} + +QColor SynHighlighterAttribute::foreground() const +{ + return mForeground; +} + +void SynHighlighterAttribute::setForeground(const QColor &foreground) +{ + if (mForeground!=foreground) { + mForeground = foreground; + setChanged(); + } +} + +SynHighlighterAttribute::SynHighlighterAttribute(const QString &name, QObject *parent): + QObject(parent), + mName(name), + mForeground(QColorConstants::Black), + mBackground(QColorConstants::White) +{ + +} + +QColor SynHighlighterAttribute::background() const +{ + return mBackground; +} + +void SynHighlighterAttribute::setBackground(const QColor &background) +{ + if (mBackground!=background) { + mBackground = background; + setChanged(); + } +} diff --git a/RedPandaIDE/qsynedit/highlighter/base.h b/RedPandaIDE/qsynedit/highlighter/base.h new file mode 100644 index 00000000..a799a0d5 --- /dev/null +++ b/RedPandaIDE/qsynedit/highlighter/base.h @@ -0,0 +1,146 @@ +#ifndef SYNHIGHLIGTERBASE_H +#define SYNHIGHLIGTERBASE_H + +#include +#include +#include +#include +#include +#include +#include +#include "../Types.h" + +typedef struct { + int state; + int spaceState; +} SynRangeState; + +typedef int SynTokenKind; + +enum class SynHighlighterTokenType { + Default, Space, Comment, + PreprocessDirective, String, StringEscapeSequence, + Identifier, Symbol, + Character, Keyword, Number}; + +enum class SynHighlighterClass { + Composition, + CppHighlighter, +}; + +class SynHighlighterAttribute : public QObject{ + Q_OBJECT +public: + explicit SynHighlighterAttribute(const QString& name, QObject* parent = nullptr); + QColor background() const; + void setBackground(const QColor &background); + + QColor foreground() const; + void setForeground(const QColor &foreground); + + QString name() const; + void setName(const QString &name); + + SynFontStyles styles() const; + void setStyles(const SynFontStyles &styles); + +signals: + void changed(); +private: + void setChanged(); +private: + QColor mBackground; + QColor mForeground; + QString mName; + SynFontStyles mStyles; +}; + +typedef std::shared_ptr PSynHighlighterAttribute; +using SynHighlighterAttributeList = QVector; + +class SynHighlighter : public QObject +{ + Q_OBJECT +public: + explicit SynHighlighter(QObject *parent = nullptr); + + const QMap& attributes() const; + + const QSet& wordBreakChars() const; + + + + PSynHighlighterAttribute commentAttribute() const; + + PSynHighlighterAttribute identifierAttribute() const; + + PSynHighlighterAttribute keywordAttribute() const; + + PSynHighlighterAttribute stringAttribute() const; + + PSynHighlighterAttribute whitespaceAttribute() const; + + PSynHighlighterAttribute symbolAttribute() const; + + virtual bool isIdentChar(const QChar& ch) const; + + virtual SynHighlighterClass getClass() const = 0; + virtual QString getName() const = 0; + + void beginUpdate(); + void endUpdate(); + virtual bool getTokenFinished() const = 0; + virtual bool isLastLineCommentNotFinished(int state) const = 0; + virtual bool isLastLineStringNotFinished(int state) const = 0; + virtual bool eol() const = 0; + virtual SynRangeState getRangeState() const; + virtual int getBraceLevel() const; + virtual int getBracketLevel() const; + virtual int getParenthesisLevel() const; + virtual QString getToken() const=0; + virtual PSynHighlighterAttribute getTokenAttribute() const=0; + virtual SynHighlighterTokenType getTokenType(); + virtual SynTokenKind getTokenKind() = 0; + virtual int getTokenPos() = 0; + virtual bool isKeyword(const QString& word); + virtual void next() = 0; + virtual void nextToEol(); + virtual void setState(SynRangeState rangeState, int braceLevel, int bracketLevel, int parenthesisLevel) = 0; + virtual void setLine(const QString& newLine, int lineNumber) = 0; + virtual void resetState() = 0; + + virtual QString languageName(); + + static bool isSpaceChar(const QChar& ch); +signals: + void attributesChanged(); + +protected: + void onAttributeChanged(); + void setAttributesChanged(); + +protected: + PSynHighlighterAttribute mCommentAttribute; + PSynHighlighterAttribute mIdentifierAttribute; + PSynHighlighterAttribute mKeywordAttribute; + PSynHighlighterAttribute mStringAttribute; + PSynHighlighterAttribute mWhitespaceAttribute; + PSynHighlighterAttribute mSymbolAttribute; + + void addAttribute(PSynHighlighterAttribute attribute); + void clearAttributes(); + virtual int attributesCount() const; + + virtual PSynHighlighterAttribute getAttribute(const QString& name) const; + +private: + QMap mAttributes; + int mUpdateCount; + bool mEnabled; + QSet mWordBreakChars; +}; + +using PSynHighlighter = std::shared_ptr; +using SynHighlighterList = QVector; + +#endif // SYNHIGHLIGTERBASE_H diff --git a/RedPandaIDE/qsynedit/highlighter/composition.cpp b/RedPandaIDE/qsynedit/highlighter/composition.cpp new file mode 100644 index 00000000..d8a16a96 --- /dev/null +++ b/RedPandaIDE/qsynedit/highlighter/composition.cpp @@ -0,0 +1,93 @@ +#include "composition.h" +#include "../Constants.h" + +SynHighlightComposition::SynHighlightComposition(QObject *parent): + SynHighlighter(parent) +{ + +} + +SynHighlighterClass SynHighlightComposition::getClass() const +{ + return SynHighlighterClass::Composition; +} + +QString SynHighlightComposition::getName() const +{ + return "SynHighlightComposition"; +} + +SynScheme::SynScheme(QObject *parent): + QObject(parent), + mCaseSensitive(true) +{ + mMarkerAttribute = std::make_shared(SYNS_AttrMarker); + connect(mMarkerAttribute.get(),&SynHighlighterAttribute::changed, + this, &SynScheme::MarkerAttriChanged); + mMarkerAttribute->setBackground(QColorConstants::Yellow); + mMarkerAttribute->setBold(true); +} + +QString SynScheme::endExpr() const +{ + return mEndExpr; +} + +void SynScheme::setEndExpr(const QString &endExpr) +{ + mEndExpr = endExpr; +} + +QString SynScheme::getStartExpr() const +{ + return StartExpr; +} + +void SynScheme::setStartExpr(const QString &value) +{ + StartExpr = value; +} + +PSynHighlighter SynScheme::getHighlighter() const +{ + return mHighlighter; +} + +void SynScheme::setHighlighter(const PSynHighlighter &highlighter) +{ + mHighlighter = highlighter; +} + +PSynHighlighterAttribute SynScheme::getMarkerAttribute() const +{ + return mMarkerAttribute; +} + +QString SynScheme::getSchemeName() const +{ + return mSchemeName; +} + +void SynScheme::setSchemeName(const QString &schemeName) +{ + mSchemeName = schemeName; +} + +int SynScheme::getCaseSensitive() const +{ + return mCaseSensitive; +} + +void SynScheme::setCaseSensitive(int caseSensitive) +{ + mCaseSensitive = caseSensitive; +} + +QString SynScheme::ConvertExpression(const QString &Value) +{ + if (!mCaseSensitive) { + return Value.toUpper(); + } else { + return Value; + } +} diff --git a/RedPandaIDE/qsynedit/highlighter/composition.h b/RedPandaIDE/qsynedit/highlighter/composition.h new file mode 100644 index 00000000..a89e08c6 --- /dev/null +++ b/RedPandaIDE/qsynedit/highlighter/composition.h @@ -0,0 +1,62 @@ +#ifndef SYNHIGHLIGHTCOMPOSITION_H +#define SYNHIGHLIGHTCOMPOSITION_H +#include "base.h" +#include +#include + +class SynSchema; +using PSynSchema = std::shared_ptr; + +using OnCheckMarker = std::function& MarkerText , int Line)>; + +class SynScheme : public QObject { + Q_OBJECT +public: + explicit SynScheme(QObject* parent=nullptr); + QString endExpr() const; + void setEndExpr(const QString &endExpr); + + QString getStartExpr() const; + void setStartExpr(const QString &value); + + PSynHighlighter getHighlighter() const; + void setHighlighter(const PSynHighlighter &highlighter); + + PSynHighlighterAttribute getMarkerAttribute() const; + + QString getSchemeName() const; + void setSchemeName(const QString &schemeName); + + int getCaseSensitive() const; + void setCaseSensitive(int caseSensitive); + +private: + QString mEndExpr; + QString StartExpr; + PSynHighlighter mHighlighter; + PSynHighlighterAttribute mMarkerAttribute; + QString mSchemeName; + int mCaseSensitive; + OnCheckMarker mOnCheckStartMarker; + OnCheckMarker mOnCheckEndMarker; + QString ConvertExpression(const QString& Value); +private slots: + void MarkerAttriChanged(); +}; + + + +class SynHighlightComposition : public SynHighlighter +{ + Q_OBJECT +public: + explicit SynHighlightComposition(QObject *parent = nullptr); + + // SynHighligterBase interface +public: + SynHighlighterClass getClass() const override; + QString getName() const override; +}; + +#endif // SYNHIGHLIGHTCOMPOSITION_H diff --git a/RedPandaIDE/qsynedit/highlighter/cpp.cpp b/RedPandaIDE/qsynedit/highlighter/cpp.cpp new file mode 100644 index 00000000..95caa4d3 --- /dev/null +++ b/RedPandaIDE/qsynedit/highlighter/cpp.cpp @@ -0,0 +1,1572 @@ +#include "cpp.h" +#include "../Constants.h" + +#include + +const QSet SynEditCppHighlighter::Keywords { + "and", + "and_eq", + "bitand", + "bitor", + "break", + "compl", + "constexpr", + "const_cast", + "continue", + "dynamic_cast", + "else", + "explicit", + "export", + "extern", + "false", + "for", + "mutable", + "noexcept", + "not", + "not_eq", + "nullptr", + "or", + "or_eq", + "register", + "reinterpret_cast", + "static_assert", + "static_cast", + "template", + "this", + "thread_local", + "true", + "typename", + "virtual", + "volatile", + "xor", + "xor_eq", + "delete", + "delete[]", + "goto", + "new", + "return", + "throw", + "using", + "case", + "default", + + "alignas", + "alignof", + "decltype", + "if", + "sizeof", + "switch", + "typeid", + "while", + + "asm", + "catch", + "do", + "namespace", + "try", + + "atomic_cancel", + "atomic_commit", + "atomic_noexcept", + "concept", + "consteval", + "constinit", + "co_wait", + "co_return", + "co_yield", + "reflexpr", + "requires", + + "auto", + "bool", + "char", + "char8_t", + "char16_t", + "char32_t", + "double", + "float", + "int", + "long", + "short", + "signed", + "unsigned", + "void", + "wchar_t", + + "const", + "inline", + + "class", + "enum", + "friend", + "operator", + "private", + "protected", + "public", + "static", + "struct", + "typedef", + "union", + + "nullptr", +}; +SynEditCppHighlighter::SynEditCppHighlighter(QObject *parent): SynHighlighter(parent) +{ + mAsmAttribute = std::make_shared(SYNS_AttrAssembler); + addAttribute(mAsmAttribute); + mCharAttribute = std::make_shared(SYNS_AttrCharacter); + addAttribute(mCharAttribute); + mCommentAttribute = std::make_shared(SYNS_AttrComment); + mCommentAttribute->setItalic(true); + addAttribute(mCommentAttribute); + mClassAttribute = std::make_shared(SYNS_AttrClass); + addAttribute(mClassAttribute); + mFloatAttribute = std::make_shared(SYNS_AttrFloat); + addAttribute(mFloatAttribute); + mFunctionAttribute = std::make_shared(SYNS_AttrFunction); + addAttribute(mFunctionAttribute); + mGlobalVarAttribute = std::make_shared(SYNS_AttrGlobalVariable); + addAttribute(mGlobalVarAttribute); + mHexAttribute = std::make_shared(SYNS_AttrHexadecimal); + addAttribute(mHexAttribute); + mIdentifierAttribute = std::make_shared(SYNS_AttrIdentifier); + addAttribute(mIdentifierAttribute); + mInvalidAttribute = std::make_shared(SYNS_AttrIllegalChar); + addAttribute(mInvalidAttribute); + mLocalVarAttribute = std::make_shared(SYNS_AttrLocalVariable); + addAttribute(mLocalVarAttribute); + mNumberAttribute = std::make_shared(SYNS_AttrNumber); + addAttribute(mNumberAttribute); + mOctAttribute = std::make_shared(SYNS_AttrOctal); + addAttribute(mOctAttribute); + mDirecAttribute = std::make_shared(SYNS_AttrPreprocessor); + addAttribute(mDirecAttribute); + mKeyAttribute = std::make_shared(SYNS_AttrReservedWord); + mKeyAttribute->setBold(true); + addAttribute(mKeyAttribute); + mWhitespaceAttribute = std::make_shared(SYNS_AttrSpace); + addAttribute(mWhitespaceAttribute); + mStringAttribute = std::make_shared(SYNS_AttrString); + addAttribute(mStringAttribute); + mStringEscapeSequenceAttribute = std::make_shared(SYNS_AttrStringEscapeSequences); + addAttribute(mStringEscapeSequenceAttribute); + mSymbolAttribute = std::make_shared(SYNS_AttrSymbol); + addAttribute(mSymbolAttribute); + mVariableAttribute = std::make_shared(SYNS_AttrVariable); + addAttribute(mVariableAttribute); + + mRange.state = RangeState::rsUnknown; + mRange.spaceState = RangeState::rsUnknown; + mParenthesisLevel = 0; + mBracketLevel = 0; + mBraceLevel = 0; + mAsmStart = false; +} + +PSynHighlighterAttribute SynEditCppHighlighter::asmAttribute() const +{ + return mAsmAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::direcAttribute() const +{ + return mDirecAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::invalidAttribute() const +{ + return mInvalidAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::keyAttribute() const +{ + return mKeyAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::numberAttribute() const +{ + return mNumberAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::floatAttribute() const +{ + return mFloatAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::hexAttribute() const +{ + return mHexAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::octAttribute() const +{ + return mOctAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::stringEscapeSequenceAttribute() const +{ + return mStringEscapeSequenceAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::charAttribute() const +{ + return mCharAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::variableAttribute() const +{ + return mVariableAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::functionAttribute() const +{ + return mFunctionAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::classAttribute() const +{ + return mClassAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::globalVarAttribute() const +{ + return mGlobalVarAttribute; +} + +PSynHighlighterAttribute SynEditCppHighlighter::localVarAttribute() const +{ + return mLocalVarAttribute; +} + +SynEditCppHighlighter::ExtTokenKind SynEditCppHighlighter::getExtTokenId() +{ + return mExtTokenId; +} + +SynTokenKind SynEditCppHighlighter::getTokenId() +{ + if ((mRange.state == RangeState::rsAsm || mRange.state == RangeState::rsAsmBlock) + && !mAsmStart && !(mTokenId == TokenKind::Comment || mTokenId == TokenKind::Space + || mTokenId == TokenKind::Null)) { + return TokenKind::Asm; + } else { + return mTokenId; + } +} + +void SynEditCppHighlighter::andSymbolProc() +{ + mTokenId = TokenKind::Symbol; + switch (mLine[mRun+1].unicode()) { + case '=': + mRun+=2; + mExtTokenId = ExtTokenKind::AndAssign; + break; + case '&': + mRun+=2; + mExtTokenId = ExtTokenKind::LogAnd; + break; + default: + mRun+=1; + mExtTokenId = ExtTokenKind::And; + } +} + +void SynEditCppHighlighter::ansiCppProc() +{ + mTokenId = TokenKind::Comment; + if (mLine[mRun]==0) { + nullProc(); + if ( (mRun<1) || (mLine[mRun-1]!='\\')) { + mRange.state = RangeState::rsUnknown; + return; + } + } + while (mLine[mRun]!=0) { + if ( isSpaceChar(mLine[mRun]) ) { + mRange.spaceState = mRange.state; + mRange.state = RangeState::rsSpace; + break; + } + mRun+=1; + } + mRange.state = RangeState::rsUnknown; + if (mLine[mRun-1] == '\\' && mLine[mRun]==0) { // continues on next line + mRange.state = RangeState::rsCppComment; + } +} + +void SynEditCppHighlighter::ansiCProc() +{ + bool finishProcess = false; + mTokenId = TokenKind::Comment; + if (mLine[mRun].unicode() == 0) { + nullProc(); + return; + } + while (mLine[mRun]!=0) { + switch(mLine[mRun].unicode()) { + case '*': + if (mLine[mRun+1] == '/') { + mRun += 2; + if (mRange.state == RangeState::rsAnsiCAsm) { + mRange.state = RangeState::rsAsm; + } else if (mRange.state == RangeState::rsAnsiCAsmBlock){ + mRange.state = RangeState::rsAsmBlock; + } else if (mRange.state == RangeState::rsDirectiveComment && + mLine[mRun] != 0 && mLine[mRun]!=13 && mLine[mRun]!=10) { + mRange.state = RangeState::rsMultiLineDirective; + } else { + mRange.state = RangeState::rsUnknown; + } + } else + mRun+=1; + case 9: + case 32: + mRange.spaceState = mRange.state; + mRange.state = RangeState::rsSpace; + finishProcess = true; + break; + default: + mRun+=1; + } + if (finishProcess) + break; + } +} + +void SynEditCppHighlighter::asciiCharProc() +{ + mTokenId = TokenKind::Char; + do { + if (isSpaceChar(mLine[mRun])) { + mRange.spaceState = RangeState::rsChar; + mRange.state = RangeState::rsSpace; + return; + } + if (mLine[mRun] == '\\') { + if (mLine[mRun+1] == '\'' || mLine[mRun+1] == '\\') { + mRun+=1; + } + } + mRun+=1; + } while (mLine[mRun]!=0 && mLine[mRun]!='\''); + if (mLine[mRun] == '\'') + mRun+=1; + mRange.state = RangeState::rsUnknown; +} + +void SynEditCppHighlighter::atSymbolProc() +{ + mTokenId = TokenKind::Unknown; + mRun+=1; +} + +void SynEditCppHighlighter::braceCloseProc() +{ + mRun += 1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::BraceClose; + if (mRange.state == RangeState::rsAsmBlock) { + mRange.state = rsUnknown; + } + mBraceLevel -= 1; +} + +void SynEditCppHighlighter::braceOpenProc() +{ + mRun += 1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::BraceOpen; + if (mRange.state == RangeState::rsAsm) { + mRange.state = RangeState::rsAsmBlock; + mAsmStart = true; + } + mBraceLevel += 1; +} + +void SynEditCppHighlighter::colonProc() +{ + mTokenId = TokenKind::Symbol; + if (mLine[mRun+1]==':') { + mRun+=2; + mExtTokenId = ExtTokenKind::ScopeResolution; + } else { + mRun+=1; + mExtTokenId = ExtTokenKind::Colon; + } +} + +void SynEditCppHighlighter::commaProc() +{ + mRun+=1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::Comma; +} + +void SynEditCppHighlighter::directiveProc() +{ + if (mLine[0]!='#') { // '#' is not first char on the line, treat it as an invalid char + mTokenId = TokenKind::Unknown; + mRun+=1; + return; + } + mTokenId = TokenKind::Directive; + do { + if (isSpaceChar(mLine[mRun])) { + mRange.spaceState = RangeState::rsMultiLineDirective; + mRange.state = RangeState::rsSpace; + return; + } + switch(mLine[mRun].unicode()) { + case '/': //comment? + switch (mLine[mRun+1].unicode()) { + case '/': // is end of directive as well + mRange.state = RangeState::rsUnknown; + return; + case '*': // might be embeded only + mRange.state = RangeState::rsDirectiveComment; + return; + } + break; + case '\\': // yet another line? + if (mLine[mRun+1] == 0) { + mRun+=1; + mRange.state = RangeState::rsMultiLineDirective; + return; + } + break; + } + mRun+=1; + } while (mLine[mRun]!=0); +} + +void SynEditCppHighlighter::directiveEndProc() +{ + mTokenId = TokenKind::Directive; + if (mLine[mRun] == 0) { + nullProc(); + return; + } + mRange.state = RangeState::rsUnknown; + do { + if (isSpaceChar(mLine[mRun])) { + mRange.spaceState = RangeState::rsMultiLineDirective; + mRange.state = RangeState::rsSpace; + return; + } + switch(mLine[mRun].unicode()) { + case '/': //comment? + switch (mLine[mRun+1].unicode()) { + case '/': // is end of directive as well + mRange.state = RangeState::rsUnknown; + return; + case '*': // might be embeded only + mRange.state = RangeState::rsDirectiveComment; + return; + } + break; + case '\\': // yet another line? + if (mLine[mRun+1] == 0) { + mRun+=1; + mRange.state = RangeState::rsMultiLineDirective; + return; + } + break; + } + mRun+=1; + } while (mLine[mRun]!=0); +} + +void SynEditCppHighlighter::equalProc() +{ + mTokenId = TokenKind::Symbol; + if (mLine[mRun+1] == '=') { + mRun += 2; + mExtTokenId = ExtTokenKind::LogEqual; + } else { + mRun += 1; + mExtTokenId = ExtTokenKind::Assign; + } +} + +void SynEditCppHighlighter::greaterProc() +{ + mTokenId = TokenKind::Symbol; + switch (mLine[mRun + 1].unicode()) { + case '=': + mRun += 2; + mExtTokenId = ExtTokenKind::GreaterThanEqual; + break; + case '>': + if (mLine[mRun+2] == '=') { + mRun+=3; + mExtTokenId = ExtTokenKind::ShiftRightAssign; + } else { + mRun += 2; + mExtTokenId = ExtTokenKind::ShiftRight; + } + break; + default: + mRun+=1; + mExtTokenId = ExtTokenKind::GreaterThan; + } +} + +void SynEditCppHighlighter::identProc() +{ + int wordEnd = mRun; + while (isIdentChar(mLine[wordEnd])) { + wordEnd+=1; + } + QString word = mLineString.mid(mRun,wordEnd-mRun); + mRun=wordEnd; + if (isKeyword(word)) { + mTokenId = TokenKind::Key; + } else { + mTokenId = TokenKind::Identifier; + } +} + +void SynEditCppHighlighter::lowerProc() +{ + mTokenId = TokenKind::Symbol; + switch(mLine[mRun+1].unicode()) { + case '=': + mRun+=2; + mExtTokenId = ExtTokenKind::LessThanEqual; + break; + case '<': + if (mLine[mRun+2] == '=') { + mRun+=3; + mExtTokenId = ExtTokenKind::ShiftLeftAssign; + } else { + mRun+=2; + mExtTokenId = ExtTokenKind::ShiftLeft; + } + break; + default: + mRun+=1; + mExtTokenId = ExtTokenKind::LessThan; + } +} + +void SynEditCppHighlighter::minusProc() +{ + mTokenId = TokenKind::Symbol; + switch(mLine[mRun+1].unicode()) { + case '=': + mRun += 2; + mExtTokenId = ExtTokenKind::SubtractAssign; + break; + case '-': + mRun += 2; + mExtTokenId = ExtTokenKind::Decrement; + break; + case '>': + mRun += 2; + mExtTokenId = ExtTokenKind::Arrow; + break; + default: + mRun += 1; + mExtTokenId = ExtTokenKind::Subtract; + } +} + +void SynEditCppHighlighter::modSymbolProc() +{ + mTokenId = TokenKind::Symbol; + switch(mLine[mRun + 1].unicode()) { + case '=': + mRun += 2; + mExtTokenId = ExtTokenKind::ModAssign; + break; + default: + mRun += 1; + mExtTokenId = ExtTokenKind::Mod; + } +} + +void SynEditCppHighlighter::notSymbolProc() +{ + mTokenId = TokenKind::Symbol; + switch(mLine[mRun + 1].unicode()) { + case '=': + mRun+=2; + mExtTokenId = ExtTokenKind::NotEqual; + break; + default: + mRun+=1; + mExtTokenId = ExtTokenKind::LogComplement; + } +} + +void SynEditCppHighlighter::nullProc() +{ + if ((mRun-1>=0) && isSpaceChar(mLine[mRun-1]) && + (mRange.state == RangeState::rsCppComment + || mRange.state == RangeState::rsDirective + || mRange.state == RangeState::rsString + || mRange.state == RangeState::rsMultiLineString + || mRange.state == RangeState::rsMultiLineDirective) ) { + mRange.state = RangeState::rsUnknown; + } else + mTokenId = TokenKind::Null; +} + +void SynEditCppHighlighter::numberProc() +{ + int idx1; // token[1] + idx1 = mRun; + mRun+=1; + mTokenId = TokenKind::Number; + bool shouldExit = false; + double x=010.140; + while (mLine[mRun]!=0) { + switch(mLine[mRun].unicode()) { + case '\'': + if (mTokenId != TokenKind::Number) { + mTokenId = TokenKind::Symbol; + return; + } + break; + case '.': + if (mLine[mRun+1] == '.') { + mRun+=2; + mTokenId = TokenKind::Unknown; + return; + } else if (mTokenId != TokenKind::Hex) { + mTokenId = TokenKind::Float; + } else { + mTokenId = TokenKind::Unknown; + return; + } + break; + case '-': + case '+': + if (mTokenId != TokenKind::Float) // number <> float. an arithmetic operator + return; + if (mLine[mRun-1]!= 'e' && mLine[mRun-1]!='E') // number = float, but no exponent. an arithmetic operator + return; + if (mLine[mRun+1]<'0' || mLine[mRun+1]>'9') {// invalid + mRun+=1; + mTokenId = TokenKind::Unknown; + return; + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + if ((mRun == idx1+1) && (mLine[idx1] == '0')) { // octal number + mTokenId = TokenKind::Octal; + } + break; + case '8': + case '9': + if ( (mLine[idx1]=='0') && (mTokenId != TokenKind::Hex) && (mTokenId != TokenKind::Float) ) // invalid octal char + mTokenId = TokenKind::Unknown; // we must continue parse, it may be an float number + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'A': + case 'B': + case 'C': + case 'D': + if (mTokenId!=TokenKind::Hex) { //invalid + mTokenId = TokenKind::Unknown; + return; + } + break; + case 'e': + case 'E': + if (mTokenId!=TokenKind::Hex) { + if (mLine[mRun-1]>='0' || mLine[mRun-1]<=9 ) {//exponent + for (int i=idx1;i='0' && mLine[mRun+1]<='9')) { + return; + } else { + mTokenId = TokenKind::Float; + } + } else { + mRun+=1; + mTokenId = TokenKind::Unknown; + return; + } + } + break; + case 'f': + case 'F': + if (mTokenId!=TokenKind::Hex) { + for (int i=idx1;i='0' && mLine[mRun+1]<='9') + || (mLine[mRun+1]>='a' && mLine[mRun+1]<='f') + || (mLine[mRun+1]>='A' && mLine[mRun+1]<='F')) ) { + mTokenId = TokenKind::Hex; + } else { + mRun+=1; + mTokenId = TokenKind::Unknown; + return; + } + break; + } + mRun+=1; + } + if (mLine[mRun-1] == '\'') { + mTokenId = TokenKind::Unknown; + } +} + +void SynEditCppHighlighter::orSymbolProc() +{ + mTokenId = TokenKind::Symbol; + switch ( mLine[mRun+1].unicode()) { + case '=': + mRun+=2; + mExtTokenId = ExtTokenKind::IncOrAssign; + break; + case '|': + mRun+=2; + mExtTokenId = ExtTokenKind::LogOr; + break; + default: + mRun+=1; + mExtTokenId = ExtTokenKind::IncOr; + } +} + +void SynEditCppHighlighter::plusProc() +{ + mTokenId = TokenKind::Symbol; + switch(mLine[mRun+1].unicode()){ + case '=': + mRun+=2; + mExtTokenId = ExtTokenKind::AddAssign; + break; + case '+': + mRun+=2; + mExtTokenId = ExtTokenKind::Increment; + break; + default: + mRun+=1; + mExtTokenId = ExtTokenKind::Add; + } +} + +void SynEditCppHighlighter::pointProc() +{ + mTokenId = TokenKind::Symbol; + if (mLine[mRun+1] == '.' && mLine[mRun+2] == '.') { + mRun+=3; + mExtTokenId = ExtTokenKind::Ellipse; + } else if (mLine[mRun+1]>=0 && mLine[mRun+1]<=9) { + numberProc(); + } else { + mRun+=1; + mExtTokenId = ExtTokenKind::Point; + } +} + +void SynEditCppHighlighter::questionProc() +{ + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::Question; + mRun+=1; +} + +void SynEditCppHighlighter::rawStringProc() +{ + bool noEscaping = false; + if (mRange.state == RangeState::rsRawStringNotEscaping) + noEscaping = true; + mTokenId = TokenKind::RawString; + mRange.state = RangeState::rsRawString; + + while (mLine[mRun]!=0) { + if ((!noEscaping) && (mLine[mRun]=='"')) { + mRun+=1; + break; + } + switch (mLine[mRun].unicode()) { + case '(': + noEscaping = true; + break; + case ')': + noEscaping = false; + break; + case ' ': + case '\t': + mRange.state = rsSpace; + if (noEscaping) { + mRange.spaceState = RangeState::rsRawStringNotEscaping; + } else { + mRange.spaceState = RangeState::rsRawStringEscaping; + } + return; + break; + } + mRun+=1; + } + mRange.state = RangeState::rsUnknown; +} + +void SynEditCppHighlighter::roundCloseProc() +{ + mRun += 1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::RoundClose; + mParenthesisLevel -= 1; +} + +void SynEditCppHighlighter::roundOpenProc() +{ + mRun += 1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::RoundOpen; + mParenthesisLevel += 1; +} + +void SynEditCppHighlighter::semiColonProc() +{ + mRun += 1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::SemiColon; + if (mRange.state == RangeState::rsAsm) + mRange.state = RangeState::rsUnknown; +} + +void SynEditCppHighlighter::slashProc() +{ + switch(mLine[mRun+1].unicode()) { + case '/': // Cpp style comment + mTokenId = TokenKind::Comment; + mRun+=2; + mRange.state = RangeState::rsCppComment; + return; + case '*': // C style comment + mTokenId = TokenKind::Comment; + if (mRange.state == RangeState::rsAsm) { + mRange.state = RangeState::rsAnsiCAsm; + } else if (mRange.state == RangeState::rsAsmBlock) { + mRange.state = RangeState::rsAnsiCAsmBlock; + } else if (mRange.state == RangeState::rsDirective) { + mRange.state = RangeState::rsDirectiveComment; + } else { + mRange.state = RangeState::rsAnsiC; + } + mRun += 2; + if (mLine[mRun]!=0) + ansiCProc(); + break; + case '=': + mRun+=2; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::DivideAssign; + break; + default: + mRun += 1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::Divide; + } +} + +void SynEditCppHighlighter::spaceProc() +{ + mRun += 1; + mTokenId = TokenKind::Space; + while (mLine[mRun]>=1 && mLine[mRun]<=32) + mRun+=1; + mRange.state = mRange.spaceState; + mRange.spaceState = RangeState::rsUnknown; +} + +void SynEditCppHighlighter::squareCloseProc() +{ + mRun+=1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::SquareClose; + mBracketLevel+=1; +} + +void SynEditCppHighlighter::squareOpenProc() +{ + mRun+=1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::SquareOpen; + mBracketLevel +=1; +} + +void SynEditCppHighlighter::starProc() +{ + mTokenId = TokenKind::Symbol; + if (mLine[mRun+1] == '=') { + mRun += 2; + mExtTokenId = ExtTokenKind::MultiplyAssign; + } else { + mRun += 1; + mExtTokenId = ExtTokenKind::Star; + } +} + +void SynEditCppHighlighter::stringEndProc() +{ + mTokenId = TokenKind::String; + if (mLine[mRun]==0) { + nullProc(); + return; + } + mRange.state = RangeState::rsUnknown; + + while (mLine[mRun]!=0) { + if (mLine[mRun]=='"') { + mRun += 1; + break; + } + if (isSpaceChar(mLine[mRun])) { + mRange.spaceState = RangeState::rsMultiLineString; + mRange.state = RangeState::rsSpace; + return; + } + if (mLine[mRun].unicode()=='\\') { + switch(mLine[mRun+1].unicode()) { + case '\'': + case '"': + case '\\': + case '?': + case 'a': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'x': + case 'u': + case 'U': + mRange.state = RangeState::rsMultiLineStringEscapeSeq; + return; + case 0: + mRun+=1; + mRange.state = RangeState::rsMultiLineString; + return; + } + } + mRun += 1; + } +} + +void SynEditCppHighlighter::stringEscapeSeqProc() +{ + mTokenId = TokenKind::StringEscapeSeq; + mRun+=1; + switch(mLine[mRun].unicode()) { + case '\'': + case '"': + case '?': + case 'a': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '\\': + mRun+=1; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + for (int i=0;i<3;i++) { + if (mLine[mRun]<'0' || mLine[mRun]>'7') + break; + mRun+=1; + } + break; + case '8': + case '9': + mTokenId = TokenKind::Unknown; + mRun+=1; + break; + case 'x': + mRun+=1; + if ( !( + (mLine[mRun]>='0' && mLine[mRun]<='9') + || (mLine[mRun]>='a' && mLine[mRun]<='f') + || (mLine[mRun]>='A' && mLine[mRun]<='F') + )) { + mTokenId = TokenKind::Unknown; + } else { + while ( + (mLine[mRun]>='0' && mLine[mRun]<='9') + || (mLine[mRun]>='a' && mLine[mRun]<='f') + || (mLine[mRun]>='A' && mLine[mRun]<='F') + ) { + mRun+=1; + } + } + break; + case 'u': + mRun+=1; + for (int i=0;i<4;i++) { + if (mLine[mRun]<'0' || mLine[mRun]>'7') { + mTokenId = TokenKind::Unknown; + return; + } + mRun+=1; + } + break; + case 'U': + mRun+=1; + for (int i=0;i<8;i++) { + if (mLine[mRun]<'0' || mLine[mRun]>'7') { + mTokenId = TokenKind::Unknown; + return; + } + mRun+=1; + } + break; + } + if (mRange.state == RangeState::rsMultiLineStringEscapeSeq) + mRange.state = RangeState::rsMultiLineString; + else + mRange.state = RangeState::rsString; +} + +void SynEditCppHighlighter::stringProc() +{ + if (mLine[mRun] == 0) { + mRange.state = RangeState::rsUnknown; + return; + } + mTokenId = TokenKind::String; + mRange.state = RangeState::rsString; + while (mLine[mRun]!=0) { + if (mLine[mRun]=='"') { + mRun+=1; + break; + } + if (isSpaceChar(mLine[mRun])) { + mRange.spaceState = RangeState::rsString; + mRange.state = RangeState::rsSpace; + return; + } + if (mLine[mRun].unicode()=='\\') { + switch(mLine[mRun+1].unicode()) { + case '\'': + case '"': + case '\\': + case '?': + case 'a': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'x': + case 'u': + case 'U': + mRange.state = RangeState::rsStringEscapeSeq; + return; + case 0: + mRun+=1; + mRange.state = RangeState::rsMultiLineString; + return; + } + } + } + mRange.state = RangeState::rsUnknown; +} + +void SynEditCppHighlighter::stringStartProc() +{ + mTokenId = TokenKind::String; + mRun += 1; + if (mLine[mRun]==0) { + mRange.state = RangeState::rsUnknown; + return; + } + stringProc(); +} + +void SynEditCppHighlighter::tildeProc() +{ + mRun+=1; + mTokenId = TokenKind::Symbol; + mExtTokenId = ExtTokenKind::BitComplement; +} + +void SynEditCppHighlighter::unknownProc() +{ + mRun+=1; + mTokenId = TokenKind::Unknown; +} + +void SynEditCppHighlighter::xorSymbolProc() +{ + mTokenId = TokenKind::Symbol; + if (mLine[mRun+1]=='=') { + mRun+=2; + mExtTokenId = ExtTokenKind::XorAssign; + } else { + mRun+=1; + mExtTokenId = ExtTokenKind::Xor; + } +} + +void SynEditCppHighlighter::processChar() +{ + switch(mLine[mRun].unicode()) { + case '&': + andSymbolProc(); + break; + case '\'': + asciiCharProc(); + break; + case '@': + atSymbolProc(); + break; + case '}': + braceCloseProc(); + break; + case '{': + braceOpenProc(); + break; + case '\r': + case '\n': + spaceProc(); + break; + case ':': + colonProc(); + break; + case ',': + commaProc(); + break; + case '#': + directiveProc(); + break; + case '=': + equalProc(); + break; + case '>': + greaterProc(); + break; + case '?': + questionProc(); + break; + case '<': + lowerProc(); + break; + case '-': + minusProc(); + break; + case '%': + modSymbolProc(); + break; + case '!': + notSymbolProc(); + break; + case 0: + nullProc(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + numberProc(); + break; + case '|': + orSymbolProc(); + break; + case '+': + plusProc(); + break; + case '.': + pointProc(); + break; + case ')': + roundCloseProc(); + break; + case '(': + roundOpenProc(); + break; + case ';': + semiColonProc(); + break; + case '/': + slashProc(); + break; + case ']': + squareCloseProc(); + break; + case '[': + squareOpenProc(); + break; + case '*': + starProc(); + break; + case '"': + stringStartProc(); + break; + case '~': + tildeProc(); + break; + case '^': + xorSymbolProc(); + break; + default: + if (isIdentChar(mLine[mRun])) { + identProc(); + } else if (isSpaceChar(mLine[mRun])) { + spaceProc(); + } else { + unknownProc(); + } + } +} + +bool SynEditCppHighlighter::getTokenFinished() const +{ + if (mTokenId == TokenKind::Comment + || mTokenId == TokenKind::String + || mTokenId == TokenKind::RawString) { + return mRange.state == RangeState::rsUnknown; + } + return true; +} + +bool SynEditCppHighlighter::isLastLineCommentNotFinished(int state) const +{ + return (state == RangeState::rsAnsiC || + state == RangeState::rsAnsiCAsm || + state == RangeState::rsAnsiCAsmBlock || + state == RangeState::rsDirectiveComment|| + state == RangeState::rsCppComment); +} + +bool SynEditCppHighlighter::isLastLineStringNotFinished(int state) const +{ + return state == RangeState::rsMultiLineString; +} + +bool SynEditCppHighlighter::eol() const +{ + return mTokenId == TokenKind::Null; +} + +QString SynEditCppHighlighter::getToken() const +{ + return mLineString.mid(mTokenPos,mRun-mTokenPos); +} + +PSynHighlighterAttribute SynEditCppHighlighter::getTokenAttribute() const +{ + switch (mTokenId) { + case TokenKind::Asm: + return mAsmAttribute; + case TokenKind::Comment: + return mCommentAttribute; + case TokenKind::Directive: + return mDirecAttribute; + case TokenKind::Identifier: + return mIdentifierAttribute; + case TokenKind::Key: + return mKeyAttribute; + case TokenKind::Number: + return mNumberAttribute; + case TokenKind::Float: + case TokenKind::HexFloat: + return mFloatAttribute; + case TokenKind::Hex: + return mHexAttribute; + case TokenKind::Octal: + return mOctAttribute; + case TokenKind::Space: + return mWhitespaceAttribute; + case TokenKind::String: + return mStringAttribute; + case TokenKind::StringEscapeSeq: + return mStringEscapeSequenceAttribute; + case TokenKind::RawString: + return mStringAttribute; + case TokenKind::Char: + return mCharAttribute; + case TokenKind::Symbol: + return mSymbolAttribute; + case TokenKind::Unknown: + return mInvalidAttribute; + default: + return mInvalidAttribute; + } +} + +SynTokenKind SynEditCppHighlighter::getTokenKind() +{ + return mTokenId; +} + +int SynEditCppHighlighter::getTokenPos() +{ + return mTokenPos; +} + +void SynEditCppHighlighter::next() +{ + mAsmStart = false; + mTokenPos = mRun; + do { + switch (mRange.state) { + case RangeState::rsAnsiC: + case RangeState::rsAnsiCAsm: + case RangeState::rsAnsiCAsmBlock: + case RangeState::rsDirectiveComment: + ansiCProc(); + break; + case RangeState::rsString: + stringProc(); + break; + case RangeState::rsCppComment: + ansiCppProc(); + break; + case RangeState::rsMultiLineDirective: + directiveEndProc(); + break; + case RangeState::rsMultiLineString: + stringEndProc(); + break; + case RangeState::rsSpace: + spaceProc(); + break; + case RangeState::rsRawStringEscaping: + case RangeState::rsRawStringNotEscaping: + rawStringProc(); + break; + case RangeState::rsStringEscapeSeq: + case RangeState::rsMultiLineStringEscapeSeq: + stringEscapeSeqProc(); + break; + case RangeState::rsChar: + if (mLine[mRun]=='\'') { + mRange.state = rsUnknown; + mTokenId = TokenKind::Char; + mRun+=1; + } else { + asciiCharProc(); + } + break; + default: + mRange.state = RangeState::rsUnknown; + if (mLine[mRun] == 'R' && mLine[mRun+1] == '"') { + mRun+=2; + rawStringProc(); + } else if ((mLine[mRun] == 'L' || mLine[mRun] == 'u' || mLine[mRun]=='U') && mLine[mRun+1]=='\"') { + mRun+=1; + stringStartProc(); + } else if (mLine[mRun] == 'u' && mLine[mRun+1] == '8' && mLine[mRun+2]=='\"') { + mRun+=2; + stringStartProc(); + } else + processChar(); + } + } while (mTokenId!=TokenKind::Null && mRun<=mTokenPos); +} + +void SynEditCppHighlighter::setLine(const QString &newLine, int lineNumber) +{ + mLineString = newLine; + mLine = mLineString.data(); + mLineNumber = lineNumber; + mRun = 0; + next(); +} + +int SynEditCppHighlighter::getBraceLevel() const +{ + return mBraceLevel; +} + +int SynEditCppHighlighter::getBracketLevel() const +{ + return mBracketLevel; +} + +int SynEditCppHighlighter::getParenthesisLevel() const +{ + return mParenthesisLevel; +} + +bool SynEditCppHighlighter::isKeyword(const QString &word) +{ + return Keywords.contains(word); +} + +SynHighlighterTokenType SynEditCppHighlighter::getTokenType() +{ + switch(mTokenId) { + case TokenKind::Comment: + return SynHighlighterTokenType::Comment; + case TokenKind::Directive: + return SynHighlighterTokenType::PreprocessDirective; + case TokenKind::Identifier: + return SynHighlighterTokenType::Identifier; + case TokenKind::Key: + return SynHighlighterTokenType::Keyword; + case TokenKind::Space: + switch (mRange.state) { + case RangeState::rsAnsiC: + case RangeState::rsAnsiCAsm: + case RangeState::rsAnsiCAsmBlock: + case RangeState::rsAsm: + case RangeState::rsAsmBlock: + case RangeState::rsDirectiveComment: + case RangeState::rsCppComment: + return SynHighlighterTokenType::Comment; + case RangeState::rsDirective: + case RangeState::rsMultiLineDirective: + return SynHighlighterTokenType::PreprocessDirective; + case RangeState::rsString: + case RangeState::rsMultiLineString: + case RangeState::rsStringEscapeSeq: + case RangeState::rsMultiLineStringEscapeSeq: + case RangeState::rsRawString: + return SynHighlighterTokenType::String; + case RangeState::rsChar : + return SynHighlighterTokenType::Character; + default: + return SynHighlighterTokenType::Space; + } + case TokenKind::String: + return SynHighlighterTokenType::String; + case TokenKind::StringEscapeSeq: + return SynHighlighterTokenType::StringEscapeSequence; + case TokenKind::RawString: + return SynHighlighterTokenType::String; + case TokenKind::Char: + return SynHighlighterTokenType::Character; + case TokenKind::Symbol: + return SynHighlighterTokenType::Symbol; + default: + return SynHighlighterTokenType::Default; + } +} + +void SynEditCppHighlighter::setState(SynRangeState rangeState, int braceLevel, int bracketLevel, int parenthesisLevel) +{ + mRange = rangeState; + mBraceLevel = braceLevel; + mBracketLevel = bracketLevel; + mParenthesisLevel = parenthesisLevel; +} + +void SynEditCppHighlighter::resetState() +{ + mRange.state = RangeState::rsUnknown; + mRange.spaceState = RangeState::rsUnknown; + mBracketLevel = 0; + mBraceLevel = 0; + mParenthesisLevel = 0; +} + +SynHighlighterClass SynEditCppHighlighter::getClass() const +{ + return SynHighlighterClass::CppHighlighter; +} + +QString SynEditCppHighlighter::getName() const +{ + return "SynCppHighlighter"; +} diff --git a/RedPandaIDE/qsynedit/highlighter/cpp.h b/RedPandaIDE/qsynedit/highlighter/cpp.h new file mode 100644 index 00000000..99265a42 --- /dev/null +++ b/RedPandaIDE/qsynedit/highlighter/cpp.h @@ -0,0 +1,212 @@ +#ifndef SYNEDITCPPHIGHLIGHTER_H +#define SYNEDITCPPHIGHLIGHTER_H +#include "base.h" +#include + +class SynEditCppHighlighter: public SynHighlighter +{ + Q_OBJECT + + enum TokenKind { + Asm = 1, + Comment, + Directive, + Identifier, + Key, + Null, + Number, + Space, + String, + StringEscapeSeq, + Symbol, + Unknown, + Char, + Float, + Hex, + HexFloat, + Octal, + RawString + }; + + enum class ExtTokenKind { + Add, AddAssign, And, AndAssign, Arrow, Assign, + BitComplement, BraceClose, BraceOpen, Colon, Comma, + Decrement, Divide, DivideAssign, Ellipse, GreaterThan, + GreaterThanEqual, IncOr, IncOrAssign, Increment, LessThan, + LessThanEqual, LogAnd, LogComplement, LogEqual, LogOr, + Mod, ModAssign, MultiplyAssign, NotEqual, Point, Question, + RoundClose, RoundOpen, ScopeResolution, SemiColon, ShiftLeft, + ShiftLeftAssign, ShiftRight, ShiftRightAssign, SquareClose, + SquareOpen, Star, Subtract, SubtractAssign, Xor, + XorAssign + }; + + enum RangeState { + rsUnknown, rsAnsiC, rsAnsiCAsm, rsAnsiCAsmBlock, rsAsm, + rsAsmBlock, rsDirective, rsDirectiveComment, rsString, + rsMultiLineString, rsMultiLineDirective, rsCppComment, + rsStringEscapeSeq, rsMultiLineStringEscapeSeq, + rsRawString, rsSpace,rsRawStringEscaping,rsRawStringNotEscaping,rsChar + }; + +public: + explicit SynEditCppHighlighter(QObject* parent = nullptr); + + PSynHighlighterAttribute asmAttribute() const; + + PSynHighlighterAttribute direcAttribute() const; + + PSynHighlighterAttribute invalidAttribute() const; + + PSynHighlighterAttribute keyAttribute() const; + + PSynHighlighterAttribute numberAttribute() const; + + PSynHighlighterAttribute floatAttribute() const; + + PSynHighlighterAttribute hexAttribute() const; + + PSynHighlighterAttribute octAttribute() const; + + PSynHighlighterAttribute stringEscapeSequenceAttribute() const; + + PSynHighlighterAttribute charAttribute() const; + + PSynHighlighterAttribute variableAttribute() const; + + PSynHighlighterAttribute functionAttribute() const; + + PSynHighlighterAttribute classAttribute() const; + + PSynHighlighterAttribute globalVarAttribute() const; + + PSynHighlighterAttribute localVarAttribute() const; + + static const QSet Keywords; + + ExtTokenKind getExtTokenId(); + SynTokenKind getTokenId(); +private: + void andSymbolProc(); + void ansiCppProc(); + void ansiCProc(); + void asciiCharProc(); + void atSymbolProc(); + void braceCloseProc(); + void braceOpenProc(); + void colonProc(); + void commaProc(); + void directiveProc(); + void directiveEndProc(); + void equalProc(); + void greaterProc(); + void identProc(); + void lowerProc(); + void minusProc(); + void modSymbolProc(); + void notSymbolProc(); + void nullProc(); + void numberProc(); + void orSymbolProc(); + void plusProc(); + void pointProc(); + void questionProc(); + void rawStringProc(); + void roundCloseProc(); + void roundOpenProc(); + void semiColonProc(); + void slashProc(); + void spaceProc(); + void squareCloseProc(); + void squareOpenProc(); + void starProc(); + void stringEndProc(); + void stringEscapeSeqProc(); + void stringProc(); + void stringStartProc(); + void tildeProc(); + void unknownProc(); + void xorSymbolProc(); + void processChar(); + +private: + bool mAsmStart; + SynRangeState mRange; +// SynRangeState mSpaceRange; + int mParenthesisLevel; + int mBracketLevel; + int mBraceLevel; + QString mLineString; + QChar* mLine; + int mLineSize; + int mRun; + int mStringLen; + int mToIdent; + int mTokenPos; + int mTokenId; + ExtTokenKind mExtTokenId; + int mLineNumber; + + PSynHighlighterAttribute mAsmAttribute; + PSynHighlighterAttribute mDirecAttribute; + PSynHighlighterAttribute mInvalidAttribute; + PSynHighlighterAttribute mKeyAttribute; + PSynHighlighterAttribute mNumberAttribute; + PSynHighlighterAttribute mFloatAttribute; + PSynHighlighterAttribute mHexAttribute; + PSynHighlighterAttribute mOctAttribute; + PSynHighlighterAttribute mStringEscapeSequenceAttribute; + PSynHighlighterAttribute mCharAttribute; + PSynHighlighterAttribute mVariableAttribute; + PSynHighlighterAttribute mFunctionAttribute; + PSynHighlighterAttribute mClassAttribute; + PSynHighlighterAttribute mGlobalVarAttribute; + PSynHighlighterAttribute mLocalVarAttribute; + + + + // SynHighligterBase interface +public: + bool getTokenFinished() const override; + bool isLastLineCommentNotFinished(int state) const override; + bool isLastLineStringNotFinished(int state) const override; + bool eol() const override; + QString getToken() const override; + PSynHighlighterAttribute getTokenAttribute() const override; + SynTokenKind getTokenKind() override; + int getTokenPos() override; + void next() override; + void setLine(const QString &newLine, int lineNumber) override; + + // SynHighligterBase interface +public: + int getBraceLevel() const override; + int getBracketLevel() const override; + int getParenthesisLevel() const override; + + // SynHighligterBase interface +public: + bool isKeyword(const QString &word) override; + + // SynHighligterBase interface +public: + SynHighlighterTokenType getTokenType() override; + + // SynHighligterBase interface +public: + void setState(SynRangeState rangeState, int braceLevel, int bracketLevel, int parenthesisLevel) override; + + // SynHighligterBase interface +public: + void resetState() override; + + // SynHighligterBase interface +public: + SynHighlighterClass getClass() const override; + + // SynHighlighter interface +public: + QString getName() const override; +}; + +#endif // SYNEDITCPPHIGHLIGHTER_H