diff --git a/include/eepp/system/filesystem.hpp b/include/eepp/system/filesystem.hpp index d2598ceae..479d5ecfd 100644 --- a/include/eepp/system/filesystem.hpp +++ b/include/eepp/system/filesystem.hpp @@ -66,7 +66,10 @@ class EE_API FileSystem { static bool fileRemove( const std::string& filepath ); /** @return The modification date of the file */ - static Uint32 fileGetModificationDate( const std::string& Filepath ); + static Uint32 fileGetModificationDate( const std::string& filepath ); + + /** @return If a file path is writeable */ + static bool fileCanWrite( const std::string& filepath ); /** If the directory path not end with a slash, it will add it. */ static void dirPathAddSlashAtEnd( std::string& path ); diff --git a/include/eepp/ui/border.hpp b/include/eepp/ui/border.hpp index 002c032f8..4de7210c6 100644 --- a/include/eepp/ui/border.hpp +++ b/include/eepp/ui/border.hpp @@ -20,6 +20,7 @@ enum class BorderType : Uint32 { Inside, Outside, Outline }; struct EE_API Border { int width = 0; Color color; + Color realColor; }; struct EE_API BorderRadiuses { diff --git a/include/eepp/ui/doc/syntaxdefinition.hpp b/include/eepp/ui/doc/syntaxdefinition.hpp index 46bacb243..4c359eb48 100644 --- a/include/eepp/ui/doc/syntaxdefinition.hpp +++ b/include/eepp/ui/doc/syntaxdefinition.hpp @@ -20,12 +20,15 @@ class EE_API SyntaxDefinition { const std::vector& patterns, const std::unordered_map& symbols = std::unordered_map(), - const std::string& comment = "" ); + const std::string& comment = "", + const std::vector headers = {} ); const std::string& getLanguageName() const; const std::vector& getFiles() const; + std::string getFileExtension() const; + const std::vector& getPatterns() const; const std::string& getComment() const; @@ -47,12 +50,17 @@ class EE_API SyntaxDefinition { /** Sets the comment string used for auto-comment functionality. */ SyntaxDefinition& setComment( const std::string& comment ); + const std::vector& getHeaders() const; + + SyntaxDefinition& setHeaders( const std::vector& headers ); + protected: std::string mLanguageName; std::vector mFiles; std::vector mPatterns; std::unordered_map mSymbols; std::string mComment; + std::vector mHeaders; }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp index 40e562842..69e5200fd 100644 --- a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp +++ b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp @@ -17,13 +17,22 @@ class EE_API SyntaxDefinitionManager { const SyntaxDefinition& getStyleByExtension( const std::string& filePath ) const; + const SyntaxDefinition& getStyleByHeader( const std::string& header ) const; + + const SyntaxDefinition& find( const std::string& filePath, const std::string& header ); + SyntaxDefinition& getStyleByExtensionRef( const std::string& filePath ); + const SyntaxDefinition& getStyleByLanguageName( const std::string& name ) const; + + SyntaxDefinition& getStyleByLanguageNameRef( const std::string& name ); + + std::vector getLanguageNames() const; + protected: SyntaxDefinitionManager(); std::vector mStyles; - SyntaxDefinition mEmptyDefinition; }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 8de754de7..45407f9a0 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -37,10 +37,12 @@ class EE_API TextDocument { enum IndentType { IndentSpaces, IndentTabs }; - static bool isNonWord( String::StringBaseType ch ); - TextDocument(); + bool isNonWord( String::StringBaseType ch ) const; + + bool hasFilepath(); + bool isEmpty(); void reset(); @@ -252,6 +254,8 @@ class EE_API TextDocument { const SyntaxDefinition& getSyntaxDefinition() const; + void setSyntaxDefinition( const SyntaxDefinition& definition ); + Uint64 getCurrentChangeId() const; const std::string& getDefaultFileName() const; @@ -274,6 +278,12 @@ class EE_API TextDocument { const String::StringBaseType& openBracket, const String::StringBaseType& closeBracket ) const; + const String& getNonWordChars() const; + + void setNonWordChars( const String& nonWordChars ); + + void resetSyntax(); + protected: friend class UndoStack; UndoStack mUndoStack; @@ -291,6 +301,7 @@ class EE_API TextDocument { Uint64 mCleanChangeId; Uint32 mPageSize{10}; std::map mCommands; + String mNonWordChars; void initializeCommands(); diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 7b48ad097..42b5faca2 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -43,7 +43,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void reset(); - void loadFromFile( const std::string& path ); + bool loadFromFile( const std::string& path ); bool save(); @@ -215,6 +215,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void setEnableColorPickerOnSelection( const bool& enableColorPickerOnSelection ); + void setSyntaxDefinition( const SyntaxDefinition& definition ); + + const SyntaxDefinition& getSyntaxDefinition() const; + /** Doc commands executed in this editor. */ TextPosition moveToLineOffset( const TextPosition& position, int offset ); diff --git a/include/eepp/ui/uifiledialog.hpp b/include/eepp/ui/uifiledialog.hpp index 79d1d2c0e..3871a9bae 100644 --- a/include/eepp/ui/uifiledialog.hpp +++ b/include/eepp/ui/uifiledialog.hpp @@ -2,6 +2,7 @@ #define EE_UI_UIFILEDIALOG_HPP #include +#include #include #include #include @@ -81,9 +82,11 @@ class EE_API UIFileDialog : public UIWindow { void setAllowFolderSelect( const bool& allowFolderSelect ); - const Uint32& getCloseWithKey() const; + const KeyBindings::Shortcut& getCloseShortcut() const; - void setCloseWithKey( const Uint32& closeWithKey ); + void setFileName( const std::string& name ); + + void setCloseShortcut( const KeyBindings::Shortcut& closeWithKey ); protected: std::string mCurPath; @@ -95,7 +98,7 @@ class EE_API UIFileDialog : public UIWindow { UITextInput* mFile; UIDropDownList* mFiletype; Uint32 mDialogFlags; - Uint32 mCloseWithKey; + KeyBindings::Shortcut mCloseShortcut; virtual void onWindowReady(); diff --git a/include/eepp/ui/uitabwidget.hpp b/include/eepp/ui/uitabwidget.hpp index 60975be9c..577026d48 100644 --- a/include/eepp/ui/uitabwidget.hpp +++ b/include/eepp/ui/uitabwidget.hpp @@ -9,13 +9,16 @@ namespace EE { namespace UI { class EE_API TabEvent : public Event { public: - TabEvent( Node* node, UITab* tabEvent, const Uint32& eventType ) : - Event( node, eventType ), tab( tabEvent ) {} + TabEvent( Node* node, UITab* tabEvent, Uint32 tabIndex, const Uint32& eventType ) : + Event( node, eventType ), tab( tabEvent ), tabIndex( tabIndex ) {} UITab* getTab() const { return tab; } + Uint32 getTabIndex() const { return tabIndex; } + protected: UITab* tab; + Uint32 tabIndex; }; class EE_API UITabWidget : public UIWidget { diff --git a/src/eepp/system/filesystem.cpp b/src/eepp/system/filesystem.cpp index 448669002..864a019f8 100644 --- a/src/eepp/system/filesystem.cpp +++ b/src/eepp/system/filesystem.cpp @@ -113,9 +113,10 @@ bool FileSystem::fileCopy( const std::string& src, const std::string& dst ) { } std::string FileSystem::fileExtension( const std::string& filepath, const bool& lowerExt ) { - size_t dotPos = filepath.find_last_of( "." ); - std::string tstr( dotPos != std::string::npos ? filepath.substr( dotPos + 1 ) - : fileNameFromPath( filepath ) ); + std::string filename( fileNameFromPath( filepath ) ); + + size_t dotPos = filename.find_last_of( "." ); + std::string tstr( dotPos != std::string::npos ? filename.substr( dotPos + 1 ) : "" ); if ( lowerExt ) String::toLowerInPlace( tstr ); @@ -174,9 +175,9 @@ bool FileSystem::fileRemove( const std::string& filepath ) { return 0 == remove( filepath.c_str() ); } -Uint32 FileSystem::fileGetModificationDate( const std::string& Filepath ) { +Uint32 FileSystem::fileGetModificationDate( const std::string& filepath ) { struct stat st; - int res = stat( Filepath.c_str(), &st ); + int res = stat( filepath.c_str(), &st ); if ( 0 == res ) return (Uint32)st.st_mtime; @@ -184,6 +185,28 @@ Uint32 FileSystem::fileGetModificationDate( const std::string& Filepath ) { return 0; } +bool FileSystem::fileCanWrite( const std::string& filepath ) { +#if EE_PLATFORM == EE_PLATFORM_WIN +#if UNICODE + return 0 == ( GetFileAttributes( String::fromUtf8( filepath ).toWideString().c_str() ) & + FILE_ATTRIBUTE_READONLY ); +#else + return 0 == ( GetFileAttributes( (LPCTSTR)filepath.c_str() ) & FILE_ATTRIBUTE_READONLY ); +#endif +#else + struct stat st; + if ( stat( filepath.c_str(), &st ) == 0 ) { + if ( st.st_uid == geteuid() ) + return ( st.st_mode & S_IWUSR ) != 0; + else if ( st.st_gid == getegid() ) + return ( st.st_mode & S_IWGRP ) != 0; + else + return ( st.st_mode & S_IWOTH ) != 0 || geteuid() == 0; + } + return false; +#endif +} + void FileSystem::dirPathAddSlashAtEnd( std::string& path ) { if ( path.size() && path[path.size() - 1] != '/' && path[path.size() - 1] != '\\' ) path += getOSSlash(); diff --git a/src/eepp/ui/doc/syntaxdefinition.cpp b/src/eepp/ui/doc/syntaxdefinition.cpp index 710b3e040..f2a0f9e49 100644 --- a/src/eepp/ui/doc/syntaxdefinition.cpp +++ b/src/eepp/ui/doc/syntaxdefinition.cpp @@ -1,4 +1,5 @@ #include +#include #include namespace EE { namespace UI { namespace Doc { @@ -9,17 +10,30 @@ SyntaxDefinition::SyntaxDefinition( const std::string& languageName, const std::vector& files, const std::vector& patterns, const std::unordered_map& symbols, - const std::string& comment ) : + const std::string& comment, + const std::vector headers ) : mLanguageName( languageName ), mFiles( files ), mPatterns( patterns ), mSymbols( symbols ), - mComment( comment ) {} + mComment( comment ), + mHeaders( headers ) {} const std::vector& SyntaxDefinition::getFiles() const { return mFiles; } +std::string SyntaxDefinition::getFileExtension() const { + if ( !mFiles.empty() ) { + std::string ext( mFiles[0] ); + String::replaceAll( ext, "%", "" ); + String::replaceAll( ext, "$", "" ); + String::replaceAll( ext, "?", "" ); + return ext; + } + return ""; +} + const std::vector& SyntaxDefinition::getPatterns() const { return mPatterns; } @@ -68,6 +82,15 @@ SyntaxDefinition& SyntaxDefinition::setComment( const std::string& comment ) { return *this; } +const std::vector& SyntaxDefinition::getHeaders() const { + return mHeaders; +} + +SyntaxDefinition& SyntaxDefinition::setHeaders( const std::vector& headers ) { + mHeaders = headers; + return *this; +} + const std::string& SyntaxDefinition::getLanguageName() const { return mLanguageName; } diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 6d91556b2..efcc8d30f 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -16,9 +16,12 @@ SINGLETON_DECLARE_IMPLEMENTATION( SyntaxDefinitionManager ) SyntaxDefinitionManager::SyntaxDefinitionManager() { // Register some languages support. + // Plain text + add( {"Plain Text", {"%.txt$"}, {}} ); + // XML - HTML add( {"XML", - {"%.xml$", "%.html?$", "%.svg?$"}, + {"%.xml$", "%.html?$", "%.svg$"}, { {{""}, "comment"}, {{"%f[^>][^<]", "%f[<]"}, "normal"}, @@ -32,7 +35,10 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { {{"%f[^<]/[%a_][%w_]*"}, "function"}, {{"[%a_][%w_]*"}, "keyword"}, {{"[/<>=]"}, "operator"}, - }} ); + }, + {}, + "", + {"<%?xml"}} ); // CSS add( {"CSS", @@ -137,7 +143,8 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { {"goto", "keyword"}, {"self", "keyword2"}, {"true", "literal"}, {"false", "literal"}, {"nil", "literal"}, }, - "--"} ); + "--", + {"^#!.*[ /]lua"}} ); // JavaScript add( {"Javascript", @@ -204,11 +211,12 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { {"self", "keyword2"}, {"None", "literal"}, {"True", "literal"}, {"False", "literal"}, }, - "#"} ); + "#", + {"^#!.*[ /]python", "^#!.*[ /]python3"}} ); // sh - bash add( {"Bash", - {"%.sh$", "%.bash$"}, + {"^#!.*bin.*sh\n", "^#!.*bin.*bash\n"}, { {{"#.*\n"}, "comment"}, {{"[[\\.]]"}, "normal"}, @@ -231,11 +239,12 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { {"while", "keyword"}, {"echo", "keyword"}, {"true", "literal"}, {"false", "literal"}, }, - "#"} ); + "#", + {"^#!.*[ /]bash", "^#!.*[ /]sh"}} ); // C++ add( {"C++", - {"%.h$", "%.inl$", "%.cpp$", "%.cc$", "%.C$", "%.cxx$", "%.c++$", "%.hh$", "%.H$", + {"%.cpp$", "%.cc$", "%.C$", "%.cxx$", "%.c++$", "%.hh$", "%.H$", "%.h$", "%.inl$", "%.hxx$", "%.hpp$", "%.h++$"}, { {{"//.-\n"}, "comment"}, @@ -362,6 +371,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { { {{"//.-\n"}, "comment"}, {{"/%*", "%*/"}, "comment"}, + {{"#.-\n"}, "comment"}, {{"\"", "\"", "\\"}, "string"}, {{"'", "'", "\\"}, "string"}, {{"%\\x[%da-fA-F]+"}, "number"}, @@ -413,7 +423,8 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { {"true", "literal"}, {"false", "literal"}, {"NULL", "literal"}, {"parent", "literal"}, {"self", "literal"}, {"echo", "function"}}, - "//"} ); + "//", + {"^#!.*[ /]php"}} ); // Add SQL std::vector keywords = { @@ -853,7 +864,7 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { {{"#", "\n"}, "comment"}, {{"\"", "\"", "\\"}, "string"}, {{"'", "'", "\\"}, "string"}, - {{"%[", "%]"}, "keyword2"}, + {{"%{", "%]"}, "keyword2"}, {{"[%a][%w_-]*%s*%f[=]"}, "keyword"}, {{"[=]"}, "operator"}, {{"https?://%S+"}, "function"}, @@ -875,6 +886,794 @@ SyntaxDefinitionManager::SyntaxDefinitionManager() { }, {}, "#"} ); + + // C# + add( {"C#", + {"%.cs$"}, + { + {{"//.-\n"}, "comment"}, + {{"/%*", "%*/"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"[%$%@]?\"", "\"", "\\"}, "string"}, + {{"'\\x%x?%x?%x?%x'"}, "string"}, + {{"'\\u%x%x%x%x'"}, "string"}, + {{"'\\?.'"}, "string"}, + {{"-?0x%x+"}, "number"}, + {{"-?%d+[%d%.eE]*f?"}, "number"}, + {{"-?%.?%d+f?"}, "number"}, + {{"[%+%-=/%*%^%%<>!~|&]"}, "operator"}, + {{"%?%?"}, "operator"}, + {{"%?%."}, "operator"}, + {{"[%a_][%w_]*%f[(]"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + }, + { + {"abstract", "keyword"}, {"as", "keyword"}, {"await", "keyword"}, + {"base", "keyword"}, {"break", "keyword"}, {"case", "keyword"}, + {"catch", "keyword"}, {"checked", "keyword"}, {"class", "keyword"}, + {"const", "keyword"}, {"continue", "keyword"}, {"default", "keyword"}, + {"delegate", "keyword"}, {"do", "keyword"}, {"else", "keyword"}, + {"enum", "keyword"}, {"event", "keyword"}, {"explicit", "keyword"}, + {"extern", "keyword"}, {"finally", "keyword"}, {"fixed", "keyword"}, + {"for", "keyword"}, {"foreach", "keyword"}, {"get", "keyword"}, + {"goto", "keyword"}, {"if", "keyword"}, {"implicit", "keyword"}, + {"in", "keyword"}, {"interface", "keyword"}, {"internal", "keyword"}, + {"is", "keyword"}, {"lock", "keyword"}, {"namespace", "keyword"}, + {"new", "keyword"}, {"operator", "keyword"}, {"out", "keyword"}, + {"override", "keyword"}, {"params", "keyword"}, {"private", "keyword"}, + {"protected", "keyword"}, {"public", "keyword"}, {"readonly", "keyword"}, + {"ref", "keyword"}, {"return", "keyword"}, {"sealed", "keyword"}, + {"set", "keyword"}, {"sizeof", "keyword"}, {"stackalloc", "keyword"}, + {"static", "keyword"}, {"struct", "keyword"}, {"switch", "keyword"}, + {"this", "keyword"}, {"throw", "keyword"}, {"try", "keyword"}, + {"typeof", "keyword"}, {"unchecked", "keyword"}, {"unsafe", "keyword"}, + {"using", "keyword"}, {"var", "keyword"}, {"virtual", "keyword"}, + {"void", "keyword"}, {"volatile", "keyword"}, {"where", "keyword"}, + {"while", "keyword"}, {"yield", "keyword"}, {"bool", "keyword2"}, + {"byte", "keyword2"}, {"char", "keyword2"}, {"decimal", "keyword2"}, + {"double", "keyword2"}, {"float", "keyword2"}, {"int", "keyword2"}, + {"long", "keyword2"}, {"object", "keyword2"}, {"sbyte", "keyword2"}, + {"short", "keyword2"}, {"string", "keyword2"}, {"uint", "keyword2"}, + {"ulong", "keyword2"}, {"ushort", "keyword2"}, {"true", "literal"}, + {"false", "literal"}, {"null", "literal"}, + }, + "//"} ); + + // Go + add( {"Go", + {"%.go$"}, + { + {{"//.-\n"}, "comment"}, + {{"/%*", "%*/"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"`", "`", "\\"}, "string"}, + {{"0[oO_][0-7]+"}, "number"}, + {{"-?0x[%x_]+"}, "number"}, + {{"-?%d+_%d"}, "number"}, + {{"-?%d+[%d%.eE]*f?"}, "number"}, + {{"-?%.?%d+f?"}, "number"}, + {{"[%+%-=/%*%^%%<>!~|&]"}, "operator"}, + {{":="}, "operator"}, + {{"[%a_][%w_]*%f[(]"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + }, + { + {"if", "keyword"}, {"else", "keyword"}, {"elseif", "keyword"}, + {"for", "keyword"}, {"continue", "keyword"}, {"return", "keyword"}, + {"struct", "keyword"}, {"switch", "keyword"}, {"case", "keyword"}, + {"default", "keyword"}, {"const", "keyword"}, {"package", "keyword"}, + {"import", "keyword"}, {"func", "keyword"}, {"var", "keyword"}, + {"type", "keyword"}, {"interface", "keyword"}, {"select", "keyword"}, + {"break", "keyword"}, {"range", "keyword"}, {"chan", "keyword"}, + {"defer", "keyword"}, {"go", "keyword"}, {"int", "keyword2"}, + {"int64", "keyword2"}, {"int32", "keyword2"}, {"int16", "keyword2"}, + {"int8", "keyword2"}, {"uint", "keyword2"}, {"uint64", "keyword2"}, + {"uint32", "keyword2"}, {"uint16", "keyword2"}, {"uint8", "keyword2"}, + {"uintptr", "keyword2"}, {"float64", "keyword2"}, {"float32", "keyword2"}, + {"map", "keyword2"}, {"string", "keyword2"}, {"rune", "keyword2"}, + {"bool", "keyword2"}, {"byte", "keyword2"}, {"error", "keyword2"}, + {"complex64", "keyword2"}, {"complex128", "keyword2"}, {"true", "literal"}, + {"false", "literal"}, {"nil", "literal"}, + }, + "//"} ); + + // Rust + add( {"Rust", + {"%.rs$"}, + { + {{"//.-\n"}, "comment"}, + {{"/%*", "%*/"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"`", "`", "\\"}, "string"}, + {{"0[oO_][0-7]+"}, "number"}, + {{"-?0x[%x_]+"}, "number"}, + {{"-?%d+_%d"}, "number"}, + {{"-?%d+[%d%.eE]*f?"}, "number"}, + {{"-?%.?%d+f?"}, "number"}, + {{"[%+%-=/%*%^%%<>!~|&]"}, "operator"}, + {{"[%a_][%w_]*%f[(]"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + }, + { + {"as", "keyword"}, {"async", "keyword"}, {"await", "keyword"}, + {"break", "keyword"}, {"const", "keyword"}, {"continue", "keyword"}, + {"crate", "keyword"}, {"dyn", "keyword"}, {"else", "keyword"}, + {"enum", "keyword"}, {"extern", "keyword"}, {"false", "keyword"}, + {"fn", "keyword"}, {"for", "keyword"}, {"if", "keyword"}, + {"impl", "keyword"}, {"in", "keyword"}, {"let", "keyword"}, + {"loop", "keyword"}, {"match", "keyword"}, {"mod", "keyword"}, + {"move", "keyword"}, {"mut", "keyword"}, {"pub", "keyword"}, + {"ref", "keyword"}, {"return", "keyword"}, {"Self", "keyword"}, + {"self", "keyword"}, {"static", "keyword"}, {"struct", "keyword"}, + {"super", "keyword"}, {"trait", "keyword"}, {"true", "keyword"}, + {"type", "keyword"}, {"unsafe", "keyword"}, {"use", "keyword"}, + {"where", "keyword"}, {"while", "keyword"}, {"i32", "keyword2"}, + {"i64", "keyword2"}, {"i128", "keyword2"}, {"i16", "keyword2"}, + {"i8", "keyword2"}, {"u16", "keyword2"}, {"u32", "keyword2"}, + {"u64", "keyword2"}, {"usize", "keyword2"}, {"isize", "keyword2"}, + {"f32", "keyword2"}, {"f64", "keyword2"}, {"f128", "keyword2"}, + {"String", "keyword2"}, {"&str", "keyword2"}, {"bool", "keyword2"}, + {"true", "literal"}, {"false", "literal"}, {"None", "literal"}, + {"Some", "literal"}, {"Option", "literal"}, {"Result", "literal"}, + }, + "//"} ); + + // GDScript + add( {"GDScript", + {"%.gd$"}, + { + {{"#.-\n"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"'", "'", "\\"}, "string"}, + {{"-?0x%x*"}, "number"}, + {{"-?%d+[%d%.e]*"}, "number"}, + {{"-?%.?%d+"}, "number"}, + {{"[%+%:%-=/%*%^%%<>!~|&]"}, "operator"}, + {{"[%a_][%w_]*%f[(]"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + }, + { + {"if", "keyword"}, + {"elif", "keyword"}, + {"else", "keyword"}, + {"for", "keyword"}, + {"while", "keyword"}, + {"match", "keyword"}, + {"break", "keyword"}, + {"continue", "keyword"}, + {"pass", "keyword"}, + {"return", "keyword"}, + {"class", "keyword"}, + {"class_name", "keyword"}, + {"extends", "keyword"}, + {"is", "keyword"}, + {"in", "keyword"}, + {"as", "keyword"}, + {"and", "keyword"}, + {"or", "keyword"}, + {"not", "keyword"}, + {"self", "keyword"}, + {"tool", "keyword"}, + {"signal", "keyword"}, + {"func", "keyword"}, + {"static", "keyword"}, + {"const", "keyword"}, + {"enum", "keyword"}, + {"var", "keyword"}, + {"onready", "keyword"}, + {"export", "keyword"}, + {"setget", "keyword"}, + {"breakpoint", "keyword"}, + {"preload", "keyword"}, + {"yield", "keyword"}, + {"assert", "keyword"}, + {"remote", "keyword"}, + {"master", "keyword"}, + {"puppet", "keyword"}, + {"remotesync", "keyword"}, + {"mastersync", "keyword"}, + {"puppetsync", "keyword"}, + {"void", "keyword2"}, + {"int", "keyword2"}, + {"float", "keyword2"}, + {"bool", "keyword2"}, + {"String", "keyword2"}, + {"Vector2", "keyword2"}, + {"Rect2", "keyword2"}, + {"Vector3", "keyword2"}, + {"Transform2D", "keyword2"}, + {"Plane", "keyword2"}, + {"Quat", "keyword2"}, + {"AABB", "keyword2"}, + {"Basis", "keyword2"}, + {"Transform", "keyword2"}, + {"Color", "keyword2"}, + {"NodePath", "keyword2"}, + {"RID", "keyword2"}, + {"Object", "keyword2"}, + {"Array", "keyword2"}, + {"PoolByteArray", "keyword2"}, + {"PoolIntArray", "keyword2"}, + {"PoolRealArray", "keyword2"}, + {"PoolStringArray", "keyword2"}, + {"PoolVector2Array", "keyword2"}, + {"PoolVector3Array", "keyword2"}, + {"PoolColorArray", "keyword2"}, + {"Dictionary", "keyword2"}, + {"null", "literal"}, + {"true", "literal"}, + {"false", "literal"}, + {"PI", "literal"}, + {"TAU", "literal"}, + {"INF", "literal"}, + {"NAN", "literal"}, + }, + "#"} ); + + // D + add( {"D", + {"%.d$", "%.di$"}, + { + {{"//.-\n"}, "comment"}, + {{"/%*", "%*/"}, "comment"}, + {{"/%+", "%+/"}, "comment"}, + {{"`", "`", "\\"}, "string"}, + {{"\"", "\"", "\\"}, "string"}, + {{"'", "'", "\\"}, "string"}, + {{"-?0x%x+"}, "number"}, + {{"-?%d+[%d%.eE]*f?"}, "number"}, + {{"-?%.?%d+f?"}, "number"}, + {{"[%+%-=/%*%^%%<>!~|&%$]+"}, "operator"}, + {{"[%a_][%w_]*!"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + {{"@safe"}, "keyword"}, + {{"@trusted"}, "keyword"}, + {{"@nogc"}, "keyword"}, + }, + { + {"abstract", "keyword"}, + {"alias", "keyword"}, + {"align", "keyword"}, + {"asm", "keyword"}, + {"assert", "keyword"}, + {"auto", "keyword"}, + {"body", "keyword"}, + {"bool", "keyword2"}, + {"break", "keyword"}, + {"byte", "keyword2"}, + {"case", "keyword"}, + {"cast", "keyword"}, + {"catch", "keyword"}, + {"cdouble", "keyword2"}, + {"cent", "keyword2"}, + {"cfloat", "keyword2"}, + {"char", "keyword2"}, + {"class", "keyword"}, + {"const", "keyword"}, + {"continue", "keyword"}, + {"creal", "keyword2"}, + {"dchar", "keyword2"}, + {"debug", "keyword"}, + {"default", "keyword"}, + {"delegate", "keyword"}, + {"deprecated", "keyword"}, + {"do", "keyword"}, + {"double", "keyword2"}, + {"else", "keyword"}, + {"enum", "keyword"}, + {"export", "keyword"}, + {"extern", "keyword"}, + {"false", "literal"}, + {"final", "keyword"}, + {"finally", "keyword"}, + {"float", "keyword2"}, + {"for", "keyword"}, + {"foreach", "keyword"}, + {"foreach_reverse", "keyword"}, + {"function", "keyword"}, + {"goto", "keyword"}, + {"idouble", "keyword2"}, + {"if", "keyword"}, + {"ifloat", "keyword2"}, + {"immutable", "keyword"}, + {"import", "keyword"}, + {"in", "keyword"}, + {"inout", "keyword"}, + {"int", "keyword2"}, + {"interface", "keyword"}, + {"invariant", "keyword"}, + {"ireal", "keyword2"}, + {"is", "keyword"}, + {"lazy", "keyword"}, + {"long", "keyword2"}, + {"macro", "keyword"}, + {"mixin", "keyword"}, + {"module", "keyword"}, + {"new", "keyword"}, + {"nothrow", "keyword"}, + {"null", "literal"}, + {"out", "keyword"}, + {"override", "keyword"}, + {"package", "keyword"}, + {"pragma", "keyword"}, + {"private", "keyword"}, + {"protected", "keyword"}, + {"public", "keyword"}, + {"pure", "keyword"}, + {"real", "keyword2"}, + {"ref", "keyword"}, + {"return", "keyword"}, + {"scope", "keyword"}, + {"shared", "keyword"}, + {"short", "keyword2"}, + {"static", "keyword"}, + {"struct", "keyword"}, + {"super", "keyword"}, + {"switch", "keyword"}, + {"synchronized", "keyword"}, + {"template", "keyword"}, + {"this", "keyword"}, + {"throw", "keyword"}, + {"true", "literal"}, + {"try", "keyword"}, + {"typeid", "keyword"}, + {"typeof", "keyword"}, + {"ubyte", "keyword2"}, + {"ucent", "keyword2"}, + {"uint", "keyword2"}, + {"ulong", "keyword2"}, + {"union", "keyword"}, + {"unittest", "keyword"}, + {"ushort", "keyword2"}, + {"version", "keyword"}, + {"void", "keyword"}, + {"wchar", "keyword2"}, + {"while", "keyword"}, + {"with", "keyword"}, + {"__FILE__", "keyword"}, + {"__FILE_FULL_PATH__", "keyword"}, + {"__MODULE__", "keyword"}, + {"__LINE__", "keyword"}, + {"__FUNCTION__", "keyword"}, + {"__PRETTY_FUNCTION__", "keyword"}, + {"__gshared", "keyword"}, + {"__traits", "keyword"}, + {"__vector", "keyword"}, + {"__parameters", "keyword"}, + }, + "//"} ); + + // Haskell + add( {"Haskell", + {"%.hs$"}, + { + {{"%-%-", "\n"}, "comment"}, + {{"{%-", "%-}"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"'", "'", "\\"}, "string"}, + {{"-?0x%x+"}, "number"}, + {{"-?%d+[%d%.eE]*f?"}, "number"}, + {{"-?%.?%d+f?"}, "number"}, + {{"[!%#%$%%&*+./%<=>%?@\\%^|%-~:]"}, "operator"}, + {{"[%a_'][%w_']*"}, "symbol"}, + }, + { + {"as", "keyword"}, {"case", "keyword"}, {"of", "keyword"}, + {"class", "keyword"}, {"data", "keyword"}, {"default", "keyword"}, + {"deriving", "keyword"}, {"do", "keyword"}, {"forall", "keyword"}, + {"foreign", "keyword"}, {"hiding", "keyword"}, {"if", "keyword"}, + {"then", "keyword"}, {"else", "keyword"}, {"import", "keyword"}, + {"infix", "keyword"}, {"infixl", "keyword"}, {"infixr", "keyword"}, + {"let", "keyword"}, {"in", "keyword"}, {"mdo", "keyword"}, + {"module", "keyword"}, {"newtype", "keyword"}, {"qualified", "keyword"}, + {"type", "keyword"}, {"where", "keyword"}, + }, + "%-%-"} ); + + // HLSL + add( {"HLSL", + { + "%.hlsl$", + }, + { + {{"//.-\n"}, "comment"}, + {{"/%*", "%*/"}, "comment"}, + {{"#", "[^\\]\n"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"'", "'", "\\"}, "string"}, + {{"-?0x%x+"}, "number"}, + {{"-?%d+[%d%.eE]*f?"}, "number"}, + {{"-?%.?%d+f?"}, "number"}, + {{"[%+%-=/%*%^%%<>!~|&]"}, "operator"}, + {{"int[1-9]x[1-9]"}, "keyword2"}, + {{"int1[0-6]x[1-9]"}, "keyword2"}, + {{"int[1-9]x1[0-6]"}, "keyword2"}, + {{"int1[0-6]x1[0-6]"}, "keyword2"}, + {{"int[1-4]"}, "keyword2"}, + {{"uint[1-9]x[1-9]"}, "keyword2"}, + {{"uint1[0-6]x[1-9]"}, "keyword2"}, + {{"uint[1-9]x1[0-6]"}, "keyword2"}, + {{"uint1[0-6]x1[0-6]"}, "keyword2"}, + {{"uint[1-4]"}, "keyword2"}, + {{"dword[1-9]x[1-9]"}, "keyword2"}, + {{"dword1[0-6]x[1-9]"}, "keyword2"}, + {{"dword[1-9]x1[0-6]"}, "keyword2"}, + {{"dword1[0-6]x1[0-6]"}, "keyword2"}, + {{"dword[1-4]"}, "keyword2"}, + {{"half[1-9]x[1-9]"}, "keyword2"}, + {{"half1[0-6]x[1-9]"}, "keyword2"}, + {{"half[1-9]x1[0-6]"}, "keyword2"}, + {{"half1[0-6]x1[0-6]"}, "keyword2"}, + {{"half[1-4]"}, "keyword2"}, + {{"float[1-9]x[1-9]"}, "keyword2"}, + {{"float1[0-6]x[1-9]"}, "keyword2"}, + {{"float[1-9]x1[0-6]"}, "keyword2"}, + {{"float1[0-6]x1[0-6]"}, "keyword2"}, + {{"float[1-4]"}, "keyword2"}, + {{"double[1-9]x[1-9]"}, "keyword2"}, + {{"double1[0-6]x[1-9]"}, "keyword2"}, + {{"double[1-9]x1[0-6]"}, "keyword2"}, + {{"double1[0-6]x1[0-6]"}, "keyword2"}, + {{"double[1-4]"}, "keyword2"}, + {{"[%a_][%w_]*%f[(]"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + }, + { + {"AppendStructuredBuffer", "keyword"}, + {"asm", "keyword"}, + {"asm_fragment", "keyword"}, + {"BlendState", "keyword2"}, + {"bool", "keyword2"}, + {"break", "keyword"}, + {"Buffer", "keyword2"}, + {"ByteAddressBuffer", "keyword2"}, + {"case", "keyword"}, + {"cbuffer", "keyword2"}, + {"centroid", "keyword2"}, + {"class", "keyword"}, + {"column_major", "keyword"}, + {"compile", "keyword"}, + {"compile_fragment", "keyword"}, + {"CompileShader", "keyword"}, + {"const", "keyword"}, + {"continue", "keyword"}, + {"ComputeShader", "keyword"}, + {"ConsumeStructuredBuffer", "keyword"}, + {"default", "keyword"}, + {"DepthStencilState", "keyword"}, + {"DepthStencilView", "keyword"}, + {"discard", "keyword"}, + {"do", "keyword"}, + {"double", "keyword2"}, + {"DomainShader", "keyword2"}, + {"dword", "keyword2"}, + {"else", "keyword"}, + {"export", "keyword"}, + {"extern", "keyword"}, + {"false", "literal"}, + {"float", "keyword2"}, + {"for", "keyword"}, + {"fxgroup", "keyword2"}, + {"GeometryShader", "keyword2"}, + {"groupshared", "keyword"}, + {"half", "keyword2"}, + {"HullShader", "keyword2"}, + {"if", "keyword"}, + {"in", "keyword"}, + {"inline", "keyword"}, + {"inout", "keyword"}, + {"InputPatch", "keyword2"}, + {"int", "keyword2"}, + {"interface", "keyword"}, + {"line", "keyword2"}, + {"lineadj", "keyword2"}, + {"linear", "keyword"}, + {"LineStream", "keyword2"}, + {"matrix", "keyword2"}, + {"min16float", "keyword2"}, + {"min10float", "keyword2"}, + {"min16int", "keyword2"}, + {"min12int", "keyword2"}, + {"min16uint", "keyword2"}, + {"namespace", "keyword"}, + {"nointerpolation", "keyword"}, + {"noperspective", "keyword"}, + {"NULL", "literal"}, + {"out", "keyword"}, + {"OutputPatch", "keyword2"}, + {"packoffset", "keyword"}, + {"pass", "keyword"}, + {"pixelfragment", "keyword"}, + {"PixelShader", "keyword2"}, + {"point", "keyword2"}, + {"PointStream", "keyword2"}, + {"precise", "keyword"}, + {"RasterizerState", "keyword2"}, + {"RenderTargetView", "keyword2"}, + {"return", "keyword"}, + {"register", "keyword"}, + {"row_major", "keyword"}, + {"RWBuffer", "keyword2"}, + {"RWByteAddressBuffer", "keyword2"}, + {"RWStructuredBuffer", "keyword2"}, + {"RWTexture1D", "keyword2"}, + {"RWTexture1DArray", "keyword2"}, + {"RWTexture2D", "keyword2"}, + {"RWTexture2DArray", "keyword2"}, + {"RWTexture3D", "keyword2"}, + {"sample", "keyword"}, + {"sampler", "keyword2"}, + {"SamplerState", "keyword2"}, + {"SamplerComparisonState", "keyword2"}, + {"shared", "keyword"}, + {"snorm", "keyword"}, + {"stateblock", "keyword"}, + {"stateblock_state", "keyword"}, + {"static", "keyword"}, + {"string", "keyword2"}, + {"struct", "keyword"}, + {"switch", "keyword"}, + {"StructuredBuffer", "keyword2"}, + {"tbuffer", "keyword2"}, + {"technique", "keyword2"}, + {"technique10", "keyword2"}, + {"technique11", "keyword2"}, + {"texture", "keyword2"}, + {"Texture1D", "keyword2"}, + {"Texture1DArray", "keyword2"}, + {"Texture2D", "keyword2"}, + {"Texture2DArray", "keyword2"}, + {"Texture2DMS", "keyword2"}, + {"Texture2DMSArray", "keyword2"}, + {"Texture3D", "keyword2"}, + {"TextureCube", "keyword2"}, + {"TextureCubeArray", "keyword2"}, + {"true", "literal"}, + {"typedef", "keyword"}, + {"triangle", "keyword2"}, + {"triangleadj", "keyword2"}, + {"TriangleStream", "keyword2"}, + {"uint", "keyword2"}, + {"uniform", "keyword"}, + {"unorm", "keyword"}, + {"unsigned", "keyword"}, + {"vector", "keyword2"}, + {"vertexfragment", "keyword2"}, + {"VertexShader", "keyword2"}, + {"void", "keyword"}, + {"volatile", "keyword"}, + {"while", "keyword"}, + {"abort", "keyword"}, + {"abs", "keyword"}, + {"acos", "keyword"}, + {"all", "keyword"}, + {"any", "keyword"}, + {"asdouble", "keyword"}, + {"asfloat", "keyword"}, + {"asin", "keyword"}, + {"asint", "keyword"}, + {"asuint", "keyword"}, + {"atan", "keyword"}, + {"atan2", "keyword"}, + {"ceil", "keyword"}, + {"clamp", "keyword"}, + {"clip", "keyword"}, + {"cos", "keyword"}, + {"cosh", "keyword"}, + {"countbits", "keyword"}, + {"cross", "keyword"}, + {"ddx", "keyword"}, + {"ddx_coarse", "keyword"}, + {"ddx_fine", "keyword"}, + {"ddy", "keyword"}, + {"ddy_coarse", "keyword"}, + {"ddy_fine", "keyword"}, + {"degrees", "keyword"}, + {"determinant", "keyword"}, + {"distance", "keyword"}, + {"dot", "keyword"}, + {"dst", "keyword"}, + {"errorf", "keyword"}, + {"exp", "keyword"}, + {"exp2", "keyword"}, + {"f16tof32", "keyword"}, + {"f32tof16", "keyword"}, + {"faceforward", "keyword"}, + {"firstbithigh", "keyword"}, + {"firstbitlow", "keyword"}, + {"floor", "keyword"}, + {"fma", "keyword"}, + {"fmod", "keyword"}, + {"frac", "keyword"}, + {"frexp", "keyword"}, + {"fwidth", "keyword"}, + {"isfinite", "keyword"}, + {"isinf", "keyword"}, + {"isnan", "keyword"}, + {"ldexp", "keyword"}, + {"length", "keyword"}, + {"lerp", "keyword"}, + {"lit", "keyword"}, + {"log", "keyword"}, + {"log10", "keyword"}, + {"log2", "keyword"}, + {"mad", "keyword"}, + {"max", "keyword"}, + {"min", "keyword"}, + {"modf", "keyword"}, + {"msad4", "keyword"}, + {"mul", "keyword"}, + {"noise", "keyword"}, + {"normalize", "keyword"}, + {"pow", "keyword"}, + {"printf", "keyword"}, + {"radians", "keyword"}, + {"rcp", "keyword"}, + {"reflect", "keyword"}, + {"refract", "keyword"}, + {"reversebits", "keyword"}, + {"round", "keyword"}, + {"rsqrt", "keyword"}, + {"saturate", "keyword"}, + {"sign", "keyword"}, + {"sin", "keyword"}, + {"sincos", "keyword"}, + {"sinh", "keyword"}, + {"smoothstep", "keyword"}, + {"sqrt", "keyword"}, + {"step", "keyword"}, + {"tan", "keyword"}, + {"tanh", "keyword"}, + {"transpose", "keyword"}, + {"trunc", "keyword"}, + }, + "//"} ); + + // LaTeX + add( {"LaTeX", + {"%.tex$"}, + { + {{"%%", "\n"}, "comment"}, + {{"&"}, "operator"}, + {{"\\\\"}, "operator"}, + {{"%$", "%$"}, "operator"}, + {{"\\%{", "\\]"}, "operator"}, + {{"{", "}"}, "keyword"}, + {{"\\%w*"}, "keyword2"}, + }, + {}, + "%%"} ); + + // Meson + add( {"Meson", + {"meson.build$"}, + { + {{"#", "\n"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"'", "'", "\\"}, "string"}, + {{"'''", "'''"}, "string"}, + {{"0x[%da-fA-F]+"}, "number"}, + {{"-?%d+%d*"}, "number"}, + {{"[%+%-=/%%%*!]"}, "operator"}, + {{"[%a_][%w_]*%f[(]"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + }, + { + {"if", "keyword"}, + {"then", "keyword"}, + {"else", "keyword"}, + {"elif", "keyword"}, + {"endif", "keyword"}, + {"foreach", "keyword"}, + {"endforeach", "keyword"}, + {"break", "keyword"}, + {"continue", "keyword"}, + {"and", "keyword"}, + {"not", "keyword"}, + {"or", "keyword"}, + {"in", "keyword"}, + {"true", "literal"}, + {"false", "literal"}, + }, + "#"} ); + + // AngelScript + add( {"AlgelScript", + {"%.as$", "%.asc$"}, + { + {{"//.-\n"}, "comment"}, + {{"/%*", "%*/"}, "comment"}, + {{"#", "[^\\]\n"}, "comment"}, + {{"\"", "\"", "\\"}, "string"}, + {{"'", "'", "\\"}, "string"}, + {{"-?0[xX]%x+"}, "number"}, + {{"-?0[bB][0-1]+"}, "number"}, + {{"-?0[oO][0-7]+"}, "number"}, + {{"-?%d+[%d%.eE]*f?"}, "number"}, + {{"-?%.?%d+f?"}, "number"}, + {{"&inout"}, "keyword"}, + {{"&in"}, "keyword"}, + {{"&out"}, "keyword"}, + {{"[%a_][%w_]*@"}, "keyword2"}, + {{"[%-%+!~@%?:&|%^<>%*/=%%]"}, "operator"}, + {{"[%a_][%w_]*%f[(]"}, "function"}, + {{"[%a_][%w_]*"}, "symbol"}, + }, + { + {"shared", "keyword"}, {"external", "keyword"}, {"private", "keyword"}, + {"protected", "keyword"}, {"const", "keyword"}, {"final", "keyword"}, + {"abstract", "keyword"}, {"class", "keyword"}, {"typedef", "keyword"}, + {"namespace", "keyword"}, {"interface", "keyword"}, {"import", "keyword"}, + {"enum", "keyword"}, {"funcdef", "keyword"}, {"get", "keyword"}, + {"set", "keyword"}, {"mixin", "keyword"}, {"void", "keyword2"}, + {"int", "keyword2"}, {"int8", "keyword2"}, {"int16", "keyword2"}, + {"int32", "keyword2"}, {"int64", "keyword2"}, {"uint", "keyword2"}, + {"uint8", "keyword2"}, {"uint16", "keyword2"}, {"uint32", "keyword2"}, + {"uint64", "keyword2"}, {"float", "keyword2"}, {"double", "keyword2"}, + {"bool", "keyword2"}, {"auto", "keyword"}, {"override", "keyword"}, + {"explicit", "keyword"}, {"property", "keyword"}, {"break", "keyword"}, + {"continue", "keyword"}, {"return", "keyword"}, {"switch", "keyword"}, + {"case", "keyword"}, {"default", "keyword"}, {"for", "keyword"}, + {"while", "keyword"}, {"do", "keyword"}, {"if", "keyword"}, + {"else", "keyword"}, {"try", "keyword"}, {"catch", "keyword"}, + {"cast", "keyword"}, {"function", "keyword"}, {"true", "literal"}, + {"false", "literal"}, {"null", "literal"}, {"is", "operator"}, + {"and", "operator"}, {"or", "operator"}, {"xor", "operator"}, + }, + "//"} ); + + + // Batch script + std::unordered_map> batchSymTable = { + {"keyword", + { + "if", "else", "elsif", "not", "for", "do", "in", "equ", + "neq", "lss", "leq", "gtr", "geq", "nul", "con", "prn", + "prn", "lpt1", "com1", "com2", "com3", "com4", "exist", "defined", + "errorlevel", "cmdextversion", "goto", "call", "verify", + }}, + {"function", + { + "set", "setlocal", "endlocal", "enabledelayedexpansion", + "echo", "type", "cd", "chdir", + "md", "mkdir", "pause", "choice", + "exit", "del", "rd", "rmdir", + "copy", "xcopy", "move", "ren", + "find", "findstr", "sort", "shift", + "attrib", "cmd", "command", "forfiles", + }}}; + + auto prepareBatchSymbols = []( std::unordered_map> table ) + -> std::unordered_map { + std::unordered_map symbols; + for ( auto el : table ) { + for ( auto it : el.second ) { + symbols[String::toLower( it )] = el.first; + symbols[String::toUpper( it )] = el.first; + } + } + return symbols; + }; + + add( {"Batch script", + {"%.bat$", "%.cmd$"}, + { + {{"@echo off\n"}, "keyword"}, + {{"@echo on\n"}, "keyword"}, + {{"rem.-\n"}, "comment"}, + {{"REM.-\n"}, "comment"}, + {{"%s*:[%w%-]+"}, "symbol"}, + {{"%:%:.-\n"}, "comment"}, + {{"%%%w+%%"}, "symbol"}, + {{"%%%%?~?[%w:]+"}, "symbol"}, + {{"[!=()%>&%^/\\@]"}, "operator"}, + {{"-?%.?%d+f?"}, "number"}, + {{"\"", "\"", "\\"}, "string"}, + {{"[%a_][%w_]*"}, "normal"}, + {{":eof"}, "keyword"}, + }, + prepareBatchSymbols( batchSymTable ), + "rem"} ); } SyntaxDefinition& SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle ) { @@ -883,13 +1682,35 @@ SyntaxDefinition& SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle ) } const SyntaxDefinition& SyntaxDefinitionManager::getPlainStyle() const { - return mEmptyDefinition; + return mStyles[0]; } SyntaxDefinition& SyntaxDefinitionManager::getStyleByExtensionRef( const std::string& filePath ) { return const_cast( getStyleByExtension( filePath ) ); } +const SyntaxDefinition& +SyntaxDefinitionManager::getStyleByLanguageName( const std::string& name ) const { + for ( auto& style : mStyles ) { + if ( style.getLanguageName() == name ) + return style; + } + return mStyles[0]; +} + +SyntaxDefinition& SyntaxDefinitionManager::getStyleByLanguageNameRef( const std::string& name ) { + return const_cast( getStyleByLanguageName( name ) ); +} + +std::vector SyntaxDefinitionManager::getLanguageNames() const { + std::vector names; + for ( auto& style : mStyles ) { + names.push_back( style.getLanguageName() ); + } + std::sort( names.begin(), names.end() ); + return names; +} + const SyntaxDefinition& SyntaxDefinitionManager::getStyleByExtension( const std::string& filePath ) const { std::string extension( FileSystem::fileExtension( filePath ) ); @@ -908,6 +1729,31 @@ SyntaxDefinitionManager::getStyleByExtension( const std::string& filePath ) cons } } } - return mEmptyDefinition; + return mStyles[0]; } + +const SyntaxDefinition& +SyntaxDefinitionManager::getStyleByHeader( const std::string& header ) const { + if ( !header.empty() ) { + for ( auto style = mStyles.rbegin(); style != mStyles.rend(); ++style ) { + for ( auto hdr : style->getHeaders() ) { + LuaPatternMatcher words( hdr ); + int start, end; + if ( words.find( header, 0, start, end ) ) { + return *style; + } + } + } + } + return mStyles[0]; +} + +const SyntaxDefinition& SyntaxDefinitionManager::find( const std::string& filePath, + const std::string& header ) { + const SyntaxDefinition& def = getStyleByExtension( filePath ); + if ( def.getLanguageName() == mStyles[0].getLanguageName() ) + return getStyleByHeader( header ); + return def; +} + }}} // namespace EE::UI::Doc diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 311a6ebfb..7180bdc77 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -14,23 +14,25 @@ namespace EE { namespace UI { namespace Doc { // Text document is loosely based on the SerenityOS (https://github.com/SerenityOS/serenity) // TextDocument and the lite editor (https://github.com/rxi/lite) implementations. -const char NON_WORD_CHARS[] = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"; +const char DEFAULT_NON_WORD_CHARS[] = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-"; -bool TextDocument::isNonWord( String::StringBaseType ch ) { - for ( size_t i = 0; i < eeARRAY_SIZE( NON_WORD_CHARS ); i++ ) { - if ( static_cast( NON_WORD_CHARS[i] ) == ch ) { - return true; - } - } - return false; +bool TextDocument::isNonWord( String::StringBaseType ch ) const { + return mNonWordChars.find_first_of( ch ) != String::InvalidPos; } TextDocument::TextDocument() : - mUndoStack( this ), mDefaultFileName( "untitled" ), mCleanChangeId( 0 ) { + mUndoStack( this ), + mDefaultFileName( "untitled" ), + mCleanChangeId( 0 ), + mNonWordChars( DEFAULT_NON_WORD_CHARS ) { initializeCommands(); reset(); } +bool TextDocument::hasFilepath() { + return mDefaultFileName != mFilePath; +} + bool TextDocument::isEmpty() { return linesCount() == 1 && line( 0 ).size() == 1; } @@ -127,6 +129,8 @@ bool TextDocument::loadFromStream( IOStream& file ) { pending -= read; blockSize = eemin( pending, BLOCK_SIZE ); }; + } else { + mLines.push_back( String( "\n" ) ); } eePRINTL( "Document \"%s\" loaded in %.2fms.", @@ -135,6 +139,11 @@ bool TextDocument::loadFromStream( IOStream& file ) { return true; } +void TextDocument::resetSyntax() { + String header( getText( {{0, 0}, positionOffset( {0, 0}, 128 )} ) ); + mSyntaxDefinition = SyntaxDefinitionManager::instance()->find( mFilePath, header ); +} + bool TextDocument::loadFromFile( const std::string& path ) { if ( !FileSystem::fileExists( path ) && PackManager::instance()->isFallbackToPacksActive() ) { std::string pathFix( path ); @@ -145,15 +154,10 @@ bool TextDocument::loadFromFile( const std::string& path ) { } } - if ( !FileSystem::fileExists( path ) ) { - mSyntaxDefinition = SyntaxDefinitionManager::instance()->getStyleByExtension( path ); - return false; - } - IOStreamFile file( path, "rb" ); bool ret = loadFromStream( file ); mFilePath = path; - mSyntaxDefinition = SyntaxDefinitionManager::instance()->getStyleByExtension( path ); + resetSyntax(); return ret; } @@ -176,10 +180,14 @@ bool TextDocument::loadFromPack( Pack* pack, std::string filePackPath ) { bool TextDocument::save( const std::string& path, const bool& utf8bom ) { if ( path.empty() || mDefaultFileName == path ) return false; - IOStreamFile file( path, "wb" ); - if ( save( file, utf8bom ) ) { + if ( FileSystem::fileCanWrite( FileSystem::fileRemoveFileName( path ) ) ) { + IOStreamFile file( path, "wb" ); mFilePath = path; - return true; + if ( save( file, utf8bom ) ) { + return true; + } else { + mFilePath.clear(); + } } return false; } @@ -944,6 +952,10 @@ const SyntaxDefinition& TextDocument::getSyntaxDefinition() const { return mSyntaxDefinition; } +void TextDocument::setSyntaxDefinition( const SyntaxDefinition& definition ) { + mSyntaxDefinition = definition; +} + Uint64 TextDocument::getCurrentChangeId() const { return mUndoStack.getCurrentChangeId(); } @@ -1104,6 +1116,14 @@ TextPosition TextDocument::findCloseBracket( TextPosition startPosition, return TextPosition(); } +const String& TextDocument::getNonWordChars() const { + return mNonWordChars; +} + +void TextDocument::setNonWordChars( const String& nonWordChars ) { + mNonWordChars = nonWordChars; +} + void TextDocument::notifyTextChanged() { for ( auto& client : mClients ) { client->onDocumentTextChanged(); diff --git a/src/eepp/ui/uiborderdrawable.cpp b/src/eepp/ui/uiborderdrawable.cpp index db39c6c37..4de06f5e2 100644 --- a/src/eepp/ui/uiborderdrawable.cpp +++ b/src/eepp/ui/uiborderdrawable.cpp @@ -99,6 +99,7 @@ Color UIBorderDrawable::getColorLeft() const { void UIBorderDrawable::setColorLeft( const Color& colorLeft ) { if ( mBorders.left.color != colorLeft ) { mBorders.left.color = colorLeft; + mBorders.left.realColor = colorLeft; mColorNeedsUpdate = true; } } @@ -110,6 +111,7 @@ Color UIBorderDrawable::getColorRight() const { void UIBorderDrawable::setColorRight( const Color& colorRight ) { if ( mBorders.right.color != colorRight ) { mBorders.right.color = colorRight; + mBorders.right.realColor = colorRight; mColorNeedsUpdate = true; } } @@ -121,6 +123,7 @@ Color UIBorderDrawable::getColorTop() const { void UIBorderDrawable::setColorTop( const Color& colorTop ) { if ( mBorders.top.color != colorTop ) { mBorders.top.color = colorTop; + mBorders.top.realColor = colorTop; mColorNeedsUpdate = true; } } @@ -132,6 +135,7 @@ Color UIBorderDrawable::getColorBottom() const { void UIBorderDrawable::setColorBottom( const Color& colorBottom ) { if ( mBorders.bottom.color != colorBottom ) { mBorders.bottom.color = colorBottom; + mBorders.bottom.realColor = colorBottom; mColorNeedsUpdate = true; } } @@ -212,15 +216,18 @@ const Borders& UIBorderDrawable::getBorders() const { } void UIBorderDrawable::onAlphaChange() { - mBorders.left.color.a = mBorders.right.color.a = mBorders.top.color.a = - mBorders.bottom.color.a = mColor.a; + mBorders.left.color.a = static_cast( mBorders.left.realColor.a * mColor.a / 255.f ); + mBorders.right.color.a = static_cast( mBorders.right.realColor.a * mColor.a / 255.f ); + mBorders.top.color.a = static_cast( mBorders.top.realColor.a * mColor.a / 255.f ); + mBorders.bottom.color.a = static_cast( mBorders.bottom.realColor.a * mColor.a / 255.f ); mColorNeedsUpdate = true; } void UIBorderDrawable::onColorFilterChange() { Drawable::onColorFilterChange(); - mBorders.left.color = mBorders.right.color = mBorders.top.color = mBorders.bottom.color = - mColor; + mBorders.left.color = mBorders.left.realColor = mBorders.right.color = + mBorders.right.realColor = mBorders.top.color = mBorders.top.realColor = + mBorders.bottom.color = mBorders.bottom.realColor = mColor; mColorNeedsUpdate = true; } diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 49d50cdb5..a9c24a797 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -192,9 +192,10 @@ void UICodeEditor::reset() { invalidateDraw(); } -void UICodeEditor::loadFromFile( const std::string& path ) { - mDoc.loadFromFile( path ); +bool UICodeEditor::loadFromFile( const std::string& path ) { + bool ret = mDoc.loadFromFile( path ); invalidateEditor(); + return ret; } bool UICodeEditor::save() { @@ -1064,6 +1065,16 @@ void UICodeEditor::setEnableColorPickerOnSelection( const bool& enableColorPicke mEnableColorPickerOnSelection = enableColorPickerOnSelection; } +void UICodeEditor::setSyntaxDefinition( const SyntaxDefinition& definition ) { + mDoc.setSyntaxDefinition( definition ); + mHighlighter.reset(); + invalidateDraw(); +} + +const SyntaxDefinition& UICodeEditor::getSyntaxDefinition() const { + return mDoc.getSyntaxDefinition(); +} + void UICodeEditor::checkMatchingBrackets() { if ( mHighlightMatchingBracket ) { mMatchingBrackets = TextRange(); @@ -1258,15 +1269,10 @@ void UICodeEditor::drawSelectionMatch( const std::pair& lineRange, String text( selectionLine.substr( selection.start().column(), selection.end().column() - selection.start().column() ) ); - if ( ( text[0] == '\t' && text.countChar( '\t' ) == text.size() ) || - ( text[0] == ' ' && text.countChar( ' ' ) == text.size() ) ) { - return; - } - for ( auto ln = lineRange.first; ln <= lineRange.second; ln++ ) { const String& line = mDoc.line( ln ).getText(); size_t pos = 0; - // Skip ridicously long lines. + // Skip ridiculously long lines. if ( line.size() > 300 ) continue; do { diff --git a/src/eepp/ui/uifiledialog.cpp b/src/eepp/ui/uifiledialog.cpp index d361027e0..3288e435c 100644 --- a/src/eepp/ui/uifiledialog.cpp +++ b/src/eepp/ui/uifiledialog.cpp @@ -22,7 +22,7 @@ UIFileDialog::UIFileDialog( Uint32 dialogFlags, std::string defaultFilePattern, UIWindow(), mCurPath( defaultDirectory ), mDialogFlags( dialogFlags ), - mCloseWithKey( KEY_UNKNOWN ) { + mCloseShortcut( KEY_UNKNOWN ) { if ( getSize().getWidth() < FDLG_MIN_WIDTH ) { mDpSize.x = FDLG_MIN_WIDTH; mSize.x = PixelDensity::dpToPxI( FDLG_MIN_WIDTH ); @@ -93,7 +93,7 @@ UIFileDialog::UIFileDialog( Uint32 dialogFlags, std::string defaultFilePattern, } ); mList->addEventListener( Event::OnItemKeyDown, [&]( const Event* event ) { const KeyEvent* KEvent = reinterpret_cast( event ); - if ( KEvent->getKeyCode() == KEY_RETURN ) { + if ( KEvent->getKeyCode() == KEY_RETURN || KEvent->getKeyCode() == KEY_KP_ENTER ) { openFileOrFolder(); } else if ( KEvent->getKeyCode() == KEY_BACKSPACE ) { goFolderUp(); @@ -489,7 +489,8 @@ UIDropDownList* UIFileDialog::getFiletypeList() const { } Uint32 UIFileDialog::onKeyUp( const KeyEvent& Event ) { - if ( mCloseWithKey && Event.getKeyCode() == mCloseWithKey ) { + if ( mCloseShortcut && Event.getKeyCode() == mCloseShortcut && + ( Event.getMod() & mCloseShortcut.mod ) ) { disableButtons(); closeWindow(); @@ -498,12 +499,18 @@ Uint32 UIFileDialog::onKeyUp( const KeyEvent& Event ) { return 1; } -const Uint32& UIFileDialog::getCloseWithKey() const { - return mCloseWithKey; +const KeyBindings::Shortcut& UIFileDialog::getCloseShortcut() const { + return mCloseShortcut; } -void UIFileDialog::setCloseWithKey( const Uint32& closeWithKey ) { - mCloseWithKey = closeWithKey; +void UIFileDialog::setFileName( const std::string& name ) { + if ( mFile ) { + mFile->setText( name ); + } +} + +void UIFileDialog::setCloseShortcut( const KeyBindings::Shortcut& closeWithKey ) { + mCloseShortcut = closeWithKey; } }} // namespace EE::UI diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index 5abef5248..346f4e2d4 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -725,10 +725,8 @@ void UINode::drawForeground() { void UINode::drawBorder() { if ( ( mFlags & UI_BORDER ) && NULL != mBorder ) { - Uint8 alpha = mBorder->getAlpha(); - mBorder->setAlpha( eemin( mAlpha * alpha / 255.f, 255 ) ); + mBorder->setAlpha( mAlpha ); mBorder->draw( mScreenPosi.asFloat(), mSize.floor() ); - mBorder->setAlpha( alpha ); } } diff --git a/src/eepp/ui/uitabwidget.cpp b/src/eepp/ui/uitabwidget.cpp index 8ba7e3cd9..59fb8a173 100644 --- a/src/eepp/ui/uitabwidget.cpp +++ b/src/eepp/ui/uitabwidget.cpp @@ -494,7 +494,7 @@ void UITabWidget::removeTab( const Uint32& index, bool destroyOwnedNode ) { orderTabs(); - TabEvent tabEvent( this, tab, Event::OnTabClosed ); + TabEvent tabEvent( this, tab, index, Event::OnTabClosed ); sendEvent( &tabEvent ); } @@ -527,7 +527,7 @@ void UITabWidget::removeAllTabs( bool destroyOwnedNode ) { for ( Uint32 i = 0; i < tabs.size(); i++ ) { if ( NULL != tabs[i] ) { - TabEvent tabEvent( this, tabs[i], Event::OnTabClosed ); + TabEvent tabEvent( this, tabs[i], i, Event::OnTabClosed ); sendEvent( &tabEvent ); } } diff --git a/src/tools/codeeditor/codeeditor.cpp b/src/tools/codeeditor/codeeditor.cpp index 2f966730a..b75b58fc8 100644 --- a/src/tools/codeeditor/codeeditor.cpp +++ b/src/tools/codeeditor/codeeditor.cpp @@ -36,7 +36,7 @@ bool App::tryTabClose( UICodeEditor* editor ) { if ( mCurEditor ) mCurEditor->setFocus(); } ); - mMsgBox->setTitle( "Close Editor Tab?" ); + mMsgBox->setTitle( "Close Tab?" ); mMsgBox->center(); mMsgBox->show(); return false; @@ -181,6 +181,15 @@ void App::applyColorScheme( const SyntaxColorScheme& colorScheme ) { updateColorSchemeMenu(); } +void App::saveDoc() { + if ( mCurEditor->getDocument().hasFilepath() ) { + if ( mCurEditor->save() ) + updateEditorState(); + } else { + saveFileDialog(); + } +} + UICodeEditor* App::createCodeEditor() { UICodeEditor* codeEditor = UICodeEditor::New(); codeEditor->setFontSize( 11 ); @@ -207,10 +216,8 @@ UICodeEditor* App::createCodeEditor() { [&] { switchPreviousSplit( mCurEditor ); } ); codeEditor->getDocument().setCommand( "switch-to-next-split", [&] { switchNextSplit( mCurEditor ); } ); - codeEditor->getDocument().setCommand( "save-doc", [&, codeEditor] { - if ( codeEditor->save() ) - updateEditorTitle( codeEditor ); - } ); + codeEditor->getDocument().setCommand( "save-doc", [&] { saveDoc(); } ); + codeEditor->getDocument().setCommand( "save-as-doc", [&] { saveFileDialog(); } ); codeEditor->getDocument().setCommand( "find", [&] { showFindView(); } ); codeEditor->getDocument().setCommand( "repeat-find", [&] { findNextText( "", mSearchBarLayout->find( "case_sensitive" )->isChecked() ); @@ -260,7 +267,7 @@ UICodeEditor* App::createCodeEditor() { codeEditor->addEventListener( Event::OnFocus, [&]( const Event* event ) { mCurEditor = event->getNode()->asType(); - updateEditorTitle( mCurEditor ); + updateEditorState(); } ); codeEditor->addEventListener( Event::OnTextChanged, [&]( const Event* event ) { updateEditorTitle( event->getNode()->asType() ); @@ -424,7 +431,7 @@ void App::onTabClosed( const TabEvent* tabEvent ) { auto d = createCodeEditorInTabWidget( tabWidget ); d.first->getTabWidget()->setTabSelected( d.first ); } else { - tabWidget->setTabSelected( tabWidget->getTabCount() - 1 ); + tabWidget->setTabSelected( eemin( tabWidget->getTabCount() - 1, tabEvent->getTabIndex() ) ); } } @@ -447,7 +454,7 @@ UITabWidget* App::createEditorWithTabWidget( Node* parent ) { tabWidget->addEventListener( Event::OnTabSelected, [&]( const Event* event ) { UITabWidget* tabWidget = event->getNode()->asType(); mCurEditor = tabWidget->getTabSelected()->getOwnedWidget()->asType(); - updateEditorTitle( mCurEditor ); + updateEditorState(); } ); tabWidget->setTabTryCloseCallback( [&]( UITab* tab ) -> bool { tryTabClose( tab->getOwnedWidget()->asType() ); @@ -477,20 +484,25 @@ void App::setAppTitle( const std::string& title ) { mWindow->setTitle( mWindowTitle + String( title.empty() ? "" : " - " + title ) ); } -void App::loadFileFromPath( const std::string& path, UICodeEditor* codeEditor ) { +bool App::loadFileFromPath( const std::string& path, UICodeEditor* codeEditor ) { + if ( FileSystem::isDirectory( path ) ) + return false; if ( NULL == codeEditor ) codeEditor = mCurEditor; codeEditor->setColorScheme( mColorSchemes[mCurrentColorScheme] ); - codeEditor->loadFromFile( path ); + bool ret = codeEditor->loadFromFile( path ); updateEditorTitle( codeEditor ); + if ( codeEditor == mCurEditor ) + updateCurrentFiletype(); + return ret; } void App::openFileDialog() { - UIFileDialog* TGDialog = UIFileDialog::New(); - TGDialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); - TGDialog->setTitle( "Open layout..." ); - TGDialog->setCloseWithKey( KEY_ESCAPE ); - TGDialog->addEventListener( Event::OpenFile, [&]( const Event* event ) { + UIFileDialog* dialog = UIFileDialog::New(); + dialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); + dialog->setTitle( "Open File" ); + dialog->setCloseShortcut( KEY_ESCAPE ); + dialog->addEventListener( Event::OpenFile, [&]( const Event* event ) { auto d = createCodeEditorInTabWidget( tabWidgetFromEditor( mCurEditor ) ); UITabWidget* tabWidget = d.first->getTabWidget(); UITab* addedTab = d.first; @@ -504,12 +516,51 @@ void App::openFileDialog() { } } } ); - TGDialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { + dialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { if ( mCurEditor && !SceneManager::instance()->isShootingDown() ) mCurEditor->setFocus(); } ); - TGDialog->center(); - TGDialog->show(); + dialog->center(); + dialog->show(); +} + +void App::saveFileDialog() { + UIFileDialog* dialog = + UIFileDialog::New( UIFileDialog::DefaultFlags | UIFileDialog::SaveDialog ); + dialog->setWinFlags( UI_WIN_DEFAULT_FLAGS | UI_WIN_MAXIMIZE_BUTTON | UI_WIN_MODAL ); + dialog->setTitle( "Save File As" ); + dialog->setCloseShortcut( KEY_ESCAPE ); + std::string filename( mCurEditor->getDocument().getFilename() ); + if ( FileSystem::fileExtension( mCurEditor->getDocument().getFilename() ).empty() ) + filename += mCurEditor->getSyntaxDefinition().getFileExtension(); + dialog->setFileName( filename ); + dialog->addEventListener( Event::SaveFile, [&]( const Event* event ) { + if ( mCurEditor ) { + std::string path( event->getNode()->asType()->getFullPath() ); + if ( !path.empty() && !FileSystem::isDirectory( path ) && + FileSystem::fileCanWrite( FileSystem::fileRemoveFileName( path ) ) ) { + if ( mCurEditor->getDocument().save( path ) ) { + updateEditorState(); + } else { + UIMessageBox* msg = + UIMessageBox::New( UIMessageBox::OK, "Couldn't write the file." ); + msg->setTitle( "Error" ); + msg->show(); + } + } else { + UIMessageBox* msg = + UIMessageBox::New( UIMessageBox::OK, "You must set a name to the file." ); + msg->setTitle( "Error" ); + msg->show(); + } + } + } ); + dialog->addEventListener( Event::OnWindowClose, [&]( const Event* ) { + if ( mCurEditor && !SceneManager::instance()->isShootingDown() ) + mCurEditor->setFocus(); + } ); + dialog->center(); + dialog->show(); } void App::findPrevText( String text, const bool& caseSensitive ) { @@ -616,6 +667,14 @@ void App::initSearchBar() { UICheckBox* caseSensitiveChk = mSearchBarLayout->find( "case_sensitive" ); findInput->getInputTextBuffer()->pushIgnoredChar( '\t' ); replaceInput->getInputTextBuffer()->pushIgnoredChar( '\t' ); + findInput->addEventListener( Event::OnTextChanged, [&, findInput]( const Event* ) { + if ( !findInput->getText().empty() ) { + findNextText( findInput->getText(), caseSensitiveChk->isChecked() ); + } else if ( mCurEditor ) { + mCurEditor->getDocument().setSelection( + mCurEditor->getDocument().getSelection().start() ); + } + } ); mSearchBarLayout->addCommand( "close-searchbar", [&] { mSearchBarLayout->setEnabled( false )->setVisible( false ); mCurEditor->setFocus(); @@ -749,6 +808,7 @@ void App::createSettingsMenu() { mSettingsMenu->add( "Save" ); mSettingsMenu->add( "Save as..." ); mSettingsMenu->addSeparator(); + mSettingsMenu->addSubMenu( "Filetype", NULL, createFiletypeMenu() ); mSettingsMenu->addSubMenu( "Color Scheme", NULL, createColorSchemeMenu() ); mSettingsMenu->addSeparator(); mSettingsMenu->add( "Close" ); @@ -771,7 +831,7 @@ void App::createSettingsMenu() { } else if ( name == "Save" ) { runCommand( "save-doc" ); } else if ( name == "Save as..." ) { - + runCommand( "save-as-doc" ); } else if ( name == "Close" ) { runCommand( "close-doc" ); } else if ( name == "Quit" ) { @@ -808,6 +868,41 @@ UIMenu* App::createColorSchemeMenu() { return mColorSchemeMenu; } +UIMenu* App::createFiletypeMenu() { + auto* dM = SyntaxDefinitionManager::instance(); + mFiletypeMenu = UIPopUpMenu::New(); + auto names = dM->getLanguageNames(); + for ( auto& name : names ) { + mFiletypeMenu->addCheckBox( + name, mCurEditor && mCurEditor->getSyntaxDefinition().getLanguageName() == name ); + } + mFiletypeMenu->addEventListener( Event::OnItemClicked, [&, dM]( const Event* event ) { + UIMenuItem* item = event->getNode()->asType(); + const String& name = item->getText(); + if ( mCurEditor ) { + mCurEditor->setSyntaxDefinition( dM->getStyleByLanguageName( name ) ); + updateCurrentFiletype(); + } + } ); + return mFiletypeMenu; +} + +void App::updateCurrentFiletype() { + if ( !mCurEditor ) + return; + std::string curLang( mCurEditor->getSyntaxDefinition().getLanguageName() ); + for ( size_t i = 0; i < mFiletypeMenu->getCount(); i++ ) { + UIMenuCheckBox* menuItem = mFiletypeMenu->getItem( i )->asType(); + std::string itemLang( menuItem->getText() ); + menuItem->setActive( curLang == itemLang ); + } +} + +void App::updateEditorState() { + updateEditorTitle( mCurEditor ); + updateCurrentFiletype(); +} + void App::init( const std::string& file ) { DisplayManager* displayManager = Engine::instance()->getDisplayManager(); Display* currentDisplay = displayManager->getDisplayIndex( 0 ); @@ -853,7 +948,6 @@ void App::init( const std::string& file ) { mTheme = UITheme::load( "uitheme", "uitheme", "", font, resPath + "assets/ui/breeze.css" ); mUISceneNode->setStyleSheet( mTheme->getStyleSheet() ); mUISceneNode->getUIThemeManager() - ->setDefaultEffectsEnabled( true ) ->setDefaultTheme( mTheme ) ->setDefaultFont( font ) ->add( mTheme ); @@ -962,7 +1056,7 @@ void App::init( const std::string& file ) { } EE_MAIN_FUNC int main( int argc, char* argv[] ) { - args::ArgumentParser parser( "eepp Code Editor" ); + args::ArgumentParser parser( "ecode" ); args::Positional file( parser, "file", "The file path" ); try { diff --git a/src/tools/codeeditor/codeeditor.hpp b/src/tools/codeeditor/codeeditor.hpp index 3e9c14f3b..8744d1bb8 100644 --- a/src/tools/codeeditor/codeeditor.hpp +++ b/src/tools/codeeditor/codeeditor.hpp @@ -43,12 +43,14 @@ class App { void init( const std::string& file = "" ); - void loadFileFromPath( const std::string& path, UICodeEditor* codeEditor = NULL ); + bool loadFileFromPath( const std::string& path, UICodeEditor* codeEditor = NULL ); void setAppTitle( const std::string& title ); void openFileDialog(); + void saveFileDialog(); + void findPrevText( String text = "", const bool& caseSensitive = true ); void findNextText( String text = "", const bool& caseSensitive = true ); @@ -98,7 +100,7 @@ class App { UISceneNode* mUISceneNode{NULL}; UICodeEditor* mCurEditor{NULL}; Console* mConsole{NULL}; - std::string mWindowTitle{"eepp - Code Editor"}; + std::string mWindowTitle{"ecode"}; UIMessageBox* mMsgBox{NULL}; String mLastSearch; UILayout* mBaseLayout{NULL}; @@ -109,6 +111,7 @@ class App { UIPopUpMenu* mSettingsMenu; UITextView* mSettingsButton; UIPopUpMenu* mColorSchemeMenu; + UIPopUpMenu* mFiletypeMenu; UITheme* mTheme; void onFileDropped( String file ); @@ -142,6 +145,14 @@ class App { void updateColorSchemeMenu(); void setColorScheme( const std::string& name ); + + UIMenu* createFiletypeMenu(); + + void updateCurrentFiletype(); + + void updateEditorState(); + + void saveDoc(); }; #endif // EE_TOOLS_CODEEDITOR_HPP