diff --git a/bin/assets/plugins/formatters.json b/bin/assets/plugins/formatters.json index 847d58aa3..2ca2fbe3c 100644 --- a/bin/assets/plugins/formatters.json +++ b/bin/assets/plugins/formatters.json @@ -7,36 +7,37 @@ }, "formatters": [ { - "file_patterns": ["%.js$", "%.ts$"], - "command": "prettier $FILENAME" + "file_patterns": ["%.js$", "%.ts$"], + "command": "prettier $FILENAME" }, { - "file_patterns": ["%.cpp$", "%.h$", "%.hpp$"], - "command": "clang-format --style=file $FILENAME" + "file_patterns": ["%.cpp$", "%.h$", "%.hpp$"], + "command": "clang-format --style=file $FILENAME" }, { - "file_patterns": ["%.py$", "%.pyw$"], - "command": "black $FILENAME", - "type": "inplace" + "file_patterns": ["%.py$", "%.pyw$"], + "command": "black $FILENAME", + "type": "inplace" }, { - "file_patterns": ["%.kt$"], - "command": "ktlint -F $FILENAME", - "type": "inplace" + "file_patterns": ["%.kt$"], + "command": "ktlint -F $FILENAME", + "type": "inplace" }, { - "file_patterns": ["%.json$", "%.cson$"], - "command": "jq -M --tab . $FILENAME" + "file_patterns": ["%.json$", "%.cson$"], + "command": "json", + "type": "native" }, { - "file_patterns": ["%.xml", "%.html?$"], - "command": "xml", - "type": "native" + "file_patterns": ["%.xml", "%.html?$"], + "command": "xml", + "type": "native" }, { - "file_patterns": ["%.css"], - "command": "css", - "type": "native" + "file_patterns": ["%.css"], + "command": "css", + "type": "native" } ] -} \ No newline at end of file +} diff --git a/bin/assets/plugins/linters.json b/bin/assets/plugins/linters.json index 455553bd5..5f4e36f3a 100644 --- a/bin/assets/plugins/linters.json +++ b/bin/assets/plugins/linters.json @@ -4,76 +4,76 @@ }, "linters": [ { - "file_patterns": ["%.php$"], - "warning_pattern": "[%a ]+:%s+(.*)%s+in%s.*on%sline%s+(%d+)", - "warning_pattern_order": { "line": 2, "col": 0, "message": 1 }, - "command": "php -l $FILENAME" + "file_patterns": ["%.php$"], + "warning_pattern": "[%a ]+:%s+(.*)%s+in%s.*on%sline%s+(%d+)", + "warning_pattern_order": { "line": 2, "col": 0, "message": 1 }, + "command": "php -l $FILENAME" }, { - "file_patterns": ["%.json$"], - "warning_pattern": "parse%s(%w*):%s(.*)at%sline%s(%d*),%scolumn%s(%d*)", - "warning_pattern_order": { "line": 3, "col": 4, "message": 2, "type": 1 }, - "command": "jq -e . $FILENAME", - "expected_exitcodes": [1, 2, 3, 4], - "no_errors_exit_code": 0, - "use_tmp_folder": true + "file_patterns": ["%.json$"], + "warning_pattern": "parse%s(%w*):%s(.*)at%sline%s(%d*),%scolumn%s(%d*)", + "warning_pattern_order": { "line": 3, "col": 4, "message": 2, "type": 1 }, + "command": "jq -e . $FILENAME", + "expected_exitcodes": [1, 2, 3, 4], + "no_errors_exit_code": 0, + "use_tmp_folder": true }, { - "file_patterns": ["%.js$", "%.ts$"], - "warning_pattern": "[^:]:(%d+):(%d+): ([^%[]+)%[([^\n]+)", - "warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 }, - "command": "eslint --no-ignore --format unix $FILENAME" + "file_patterns": ["%.js$", "%.ts$"], + "warning_pattern": "[^:]:(%d+):(%d+): ([^%[]+)%[([^\n]+)", + "warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 }, + "command": "eslint --no-ignore --format unix $FILENAME" }, { - "file_patterns": ["%.lua$"], - "warning_pattern": "[^:]:(%d+):(%d+):[%s]?([^\n]+)", - "command": "luacheck $FILENAME --formatter=plain -g --no-max-line-length" + "file_patterns": ["%.lua$"], + "warning_pattern": "[^:]:(%d+):(%d+):[%s]?([^\n]+)", + "command": "luacheck $FILENAME --formatter=plain -g --no-max-line-length" }, { - "file_patterns": ["%.py$"], - "warning_pattern": "[^:]:(%d+):(%d+):%s([^\n]+)", - "command": "pycodestyle --ignore=E402 $FILENAME" + "file_patterns": ["%.py$"], + "warning_pattern": "[^:]:(%d+):(%d+):%s([^\n]+)", + "command": "pycodestyle --ignore=E402 $FILENAME" }, { - "file_patterns": ["%.sh$"], - "warning_pattern": "[^:]:(%d+):(%d+):%s?([^%s]*)([^\n]*)", - "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, - "command": "shellcheck -f gcc $FILENAME" + "file_patterns": ["%.sh$"], + "warning_pattern": "[^:]:(%d+):(%d+):%s?([^%s]*)([^\n]*)", + "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, + "command": "shellcheck -f gcc $FILENAME" }, { - "file_patterns": ["%.sol$"], - "warning_pattern": "(%d+):(%d+)%s.(%w*)%s.([^\n]*)", - "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, - "command": "solhint $FILENAME" + "file_patterns": ["%.sol$"], + "warning_pattern": "(%d+):(%d+)%s.(%w*)%s.([^\n]*)", + "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, + "command": "solhint $FILENAME" }, { - "file_patterns": ["%.cpp$", "%.hpp$", "%.cxx$", "%.hxx$"], - "warning_pattern": "$FILENAME:(%d+):(%d+):%s?([^%s]*)([^\n]*)", - "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, - "command": "cppcheck --language=c++ --enable=all --template=gcc $FILENAME" + "file_patterns": ["%.cpp$", "%.hpp$", "%.cxx$", "%.hxx$"], + "warning_pattern": "$FILENAME:(%d+):(%d+):%s?([^%s]*)([^\n]*)", + "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, + "command": "cppcheck --language=c++ --enable=all --template=gcc $FILENAME" }, { - "file_patterns": ["%.kt$"], - "warning_pattern": "[^:]:(%d+):(%d+):%s([^\n]+)", - "warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 }, - "command": "ktlint $FILENAME" + "file_patterns": ["%.kt$"], + "warning_pattern": "[^:]:(%d+):(%d+):%s([^\n]+)", + "warning_pattern_order": { "line": 1, "col": 2, "message": 3, "type": 4 }, + "command": "ktlint $FILENAME" }, { - "file_patterns": ["%.zig$"], - "warning_pattern": "[^%s:]:(%d+):(%d+):[%s]?(%w*):([^\n]*)", - "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, - "command": "zig ast-check $FILENAME", - "deduplicate": true, - "expected_exitcodes": [0, 1] + "file_patterns": ["%.zig$"], + "warning_pattern": "[^%s:]:(%d+):(%d+):[%s]?(%w*):([^\n]*)", + "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, + "command": "zig ast-check $FILENAME", + "deduplicate": true, + "expected_exitcodes": [0, 1] }, { - "file_patterns": ["%.nim$", "%.nims$"], - "warning_pattern": "$FILENAME%((%d+), (%d+)%)%s(%w*):%s([^\n]+[^/]*)", - "command": "nim --listfullpaths --stdout check $FILENAME", - "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, - "deduplicate": true, - "expected_exitcodes": [0, 1], - "use_tmp_folder": true + "file_patterns": ["%.nim$", "%.nims$"], + "warning_pattern": "$FILENAME%((%d+), (%d+)%)%s(%w*):%s([^\n]+[^/]*)", + "command": "nim --listfullpaths --stdout check $FILENAME", + "warning_pattern_order": { "line": 1, "col": 2, "message": 4, "type": 3 }, + "deduplicate": true, + "expected_exitcodes": [0, 1], + "use_tmp_folder": true } ] } \ No newline at end of file diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 6963d12f0..c9c24b3a2 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -79,6 +79,10 @@ class UICodeEditorPlugin { virtual void minimapDrawAfterLineText( UICodeEditor*, const Int64&, const Vector2f&, const Vector2f&, const Float&, const Float& ){}; + virtual void drawGutter( UICodeEditor* /*editor*/, const Int64& /*index*/, + const Vector2f& /*screenStart*/, const Float& /*lineHeight*/, + const Float& /*gutterWidth*/, const Float& /*fontSize*/ ){}; + Uint32 addOnReadyCallback( const OnReadyCb& cb ) { mOnReadyCallbacks[mReadyCbNum++] = cb; return mReadyCbNum; @@ -523,6 +527,14 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Float getLineOffset() const; + /** Register a gutter space to be used by a plugin. + * @param plugin Plugin requesting it + * @param pixels Amount of pixels to request in the gutter + * @param order Order goes from left (lower number) to right (bigger number). */ + bool registerGutterSpace( UICodeEditorPlugin* plugin, const Float& pixels, int order ); + + bool unregisterGutterSpace( UICodeEditorPlugin* plugin ); + protected: struct LastXOffset { TextPosition position; @@ -612,6 +624,13 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { }; mutable std::map mTextCache; Tools::UIDocFindReplace* mFindReplace{ nullptr }; + struct PluginGutterSpace { + UICodeEditorPlugin* plugin; + Float space; + int order; + }; + std::vector mPluginGutterSpaces; + Float mPluginsGutterSpace{ 0 }; UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands = true, const bool& autoRegisterBaseKeybindings = true ); @@ -784,6 +803,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void updateLineCache( const Int64& lineIndex ); void findReplace(); + + bool gutterSpaceExists( UICodeEditorPlugin* plugin ) const; }; }} // namespace EE::UI diff --git a/include/eepp/ui/uiiconthememanager.hpp b/include/eepp/ui/uiiconthememanager.hpp index 904facb7b..d358d0d68 100644 --- a/include/eepp/ui/uiiconthememanager.hpp +++ b/include/eepp/ui/uiiconthememanager.hpp @@ -10,7 +10,8 @@ class UIThemeManager; class EE_API UIIconThemeManager { public: - static std::string getIconNameFromFileName( const std::string& fileName ); + static std::string getIconNameFromFileName( const std::string& fileName, + bool retOnlyWithExtension = false ); static UIIconThemeManager* New(); @@ -36,9 +37,9 @@ class EE_API UIIconThemeManager { protected: std::vector mIconThemes; - UIIconTheme* mCurrentTheme{nullptr}; - UIIconTheme* mFallbackTheme{nullptr}; - UIThemeManager* mFallbackThemeManager{nullptr}; + UIIconTheme* mCurrentTheme{ nullptr }; + UIIconTheme* mFallbackTheme{ nullptr }; + UIThemeManager* mFallbackThemeManager{ nullptr }; UIIconThemeManager(); diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index b5b50ba1f..606a4bc6e 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -268,28 +268,36 @@ void UICodeEditor::draw() { } for ( unsigned long i = lineRange.first; i <= lineRange.second; i++ ) { - for ( auto& plugin : mPlugins ) - plugin->drawBeforeLineText( - this, i, - { startScroll.x, static_cast( startScroll.y + lineHeight * (double)i ) }, - charSize, lineHeight ); - - drawLineText( - i, { startScroll.x, static_cast( startScroll.y + lineHeight * (double)i ) }, - charSize, lineHeight ); + Vector2f curScroll( + { startScroll.x, static_cast( startScroll.y + lineHeight * (double)i ) } ); for ( auto& plugin : mPlugins ) - plugin->drawAfterLineText( - this, i, - { startScroll.x, static_cast( startScroll.y + lineHeight * (double)i ) }, - charSize, lineHeight ); + plugin->drawBeforeLineText( this, i, curScroll, charSize, lineHeight ); + + drawLineText( i, curScroll, charSize, lineHeight ); + + for ( auto& plugin : mPlugins ) + plugin->drawAfterLineText( this, i, curScroll, charSize, lineHeight ); + + if ( mPluginsGutterSpace > 0 ) { + Float curGutterPos = 0.f; + for ( auto& plugin : mPluginGutterSpaces ) { + for ( unsigned long i = lineRange.first; i <= lineRange.second; i++ ) { + plugin.plugin->drawGutter( this, i, + { screenStart.x + curGutterPos, curScroll.y }, + lineHeight, plugin.space, charSize ); + } + curGutterPos += plugin.space; + } + } } drawCursor( startScroll, lineHeight, cursor ); if ( mShowLineNumber ) { - drawLineNumbers( lineRange, startScroll, screenStart, lineHeight, gutterWidth, - lineNumberDigits, charSize ); + drawLineNumbers( lineRange, startScroll, + { screenStart.x + mPluginsGutterSpace, screenStart.y }, lineHeight, + getLineNumberWidth(), lineNumberDigits, charSize ); } if ( mColorPreview && mPreviewColorRange.isValid() && isMouseOver() && !mMinimapHover ) { @@ -648,7 +656,7 @@ Float UICodeEditor::getLineNumberWidth() const { } Float UICodeEditor::getGutterWidth() const { - return getLineNumberWidth(); + return getLineNumberWidth() + mPluginsGutterSpace; } const bool& UICodeEditor::getShowLineNumber() const { @@ -2077,6 +2085,38 @@ Float UICodeEditor::getLineOffset() const { return eeceil( convertLength( mLineSpacing, mSize.getWidth() ) * 0.5f ); } +bool UICodeEditor::gutterSpaceExists( UICodeEditorPlugin* plugin ) const { + for ( const auto& space : mPluginGutterSpaces ) { + if ( space.plugin == plugin ) + return true; + } + return false; +} + +bool UICodeEditor::registerGutterSpace( UICodeEditorPlugin* plugin, const Float& pixels, + int order ) { + if ( gutterSpaceExists( plugin ) ) + return false; + mPluginGutterSpaces.push_back( { plugin, pixels, order } ); + mPluginsGutterSpace += pixels; + std::sort( mPluginGutterSpaces.begin(), mPluginGutterSpaces.end(), + []( const PluginGutterSpace& left, const PluginGutterSpace& right ) { + return left.order < right.order; + } ); + return true; +} + +bool UICodeEditor::unregisterGutterSpace( UICodeEditorPlugin* plugin ) { + for ( size_t i = 0; i < mPluginGutterSpaces.size(); ++i ) { + if ( mPluginGutterSpaces[i].plugin == plugin ) { + mPluginsGutterSpace -= mPluginGutterSpaces[i].space; + mPluginGutterSpaces.erase( mPluginGutterSpaces.begin() + i ); + return true; + } + } + return false; +} + Float UICodeEditor::getCharacterSize() const { return PixelDensity::dpToPx( mFontStyleConfig.getFontCharacterSize() ); } diff --git a/src/eepp/ui/uiiconthememanager.cpp b/src/eepp/ui/uiiconthememanager.cpp index db9391e1d..54357498a 100644 --- a/src/eepp/ui/uiiconthememanager.cpp +++ b/src/eepp/ui/uiiconthememanager.cpp @@ -5,13 +5,15 @@ namespace EE { namespace UI { -std::string UIIconThemeManager::getIconNameFromFileName( const std::string& fileName ) { +std::string UIIconThemeManager::getIconNameFromFileName( const std::string& fileName, + bool retOnlyWithExtension ) { std::string ext( FileSystem::fileExtension( fileName ) ); if ( !ext.empty() ) { return "filetype-" + ext; - } else { + } else if ( !retOnlyWithExtension ) { return "filetype-" + String::toLower( fileName ); } + return "file"; } UIIconThemeManager* UIIconThemeManager::New() { diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index caa6ba23a..e589ca116 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -417,7 +417,12 @@ void AppConfig::loadProject( std::string projectFolder, UICodeEditorSplitter* ed eemax( 0, ini.getValueI( "document", "line_breaking_column", 100 ) ); if ( ini.keyValueExists( "nodes", "documents" ) ) { - json j = json::parse( ini.getValue( "nodes", "documents" ) ); + json j; + try { + j = json::parse( ini.getValue( "nodes", "documents" ) ); + } catch ( ... ) { + return; + } if ( j.is_discarded() ) return; loadDocuments( editorSplitter, pool, j, diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index f720166ec..ea9d4a103 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -20,6 +20,7 @@ namespace ecode { Clock globalClock; bool firstFrame = true; +bool firstUpdate = true; App* appInstance = nullptr; void appLoop() { @@ -453,13 +454,17 @@ void App::closeApp() { void App::mainLoop() { mWindow->getInput()->update(); SceneManager::instance()->update(); + if ( firstUpdate ) { + Log::info( "First update took: %.2f ms", globalClock.getElapsedTime().asMilliseconds() ); + firstUpdate = false; + } if ( SceneManager::instance()->getUISceneNode()->invalidated() || mBenchmarkMode ) { mWindow->clear(); SceneManager::instance()->draw(); mWindow->display(); if ( firstFrame ) { - Log::info( "First frame took: %.2f ms", globalClock.getElapsed().asMilliseconds() ); + Log::info( "First frame took: %.2f ms", globalClock.getElapsedTime().asMilliseconds() ); firstFrame = false; } } else { @@ -2287,21 +2292,20 @@ void App::createDocAlert( UICodeEditor* editor ) { if ( docAlert ) return; - // TODO: Add text translations const std::string& msg = R"xml( - - - )xml"; - docAlert = static_cast( mUISceneNode->loadLayoutFromString( msg, editor ) ); + docAlert = mUISceneNode->loadLayoutFromString( msg, editor )->asType(); editor->enableReportSizeChangeToChilds(); @@ -3684,7 +3688,7 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe - + @@ -3703,8 +3707,8 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe - - + + @@ -3840,6 +3844,7 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe { "filetype-h", 61792 }, { "filetype-cs", 61720 }, { "filetype-cpp", 61719 }, + { "filetype-hpp", 61719 }, { "filetype-css", 61743 }, { "filetype-conf", 61781 }, { "filetype-cfg", 61781 }, diff --git a/src/tools/ecode/plugins/formatter/formatterplugin.cpp b/src/tools/ecode/plugins/formatter/formatterplugin.cpp index 58537924e..93242f85f 100644 --- a/src/tools/ecode/plugins/formatter/formatterplugin.cpp +++ b/src/tools/ecode/plugins/formatter/formatterplugin.cpp @@ -120,7 +120,13 @@ void FormatterPlugin::loadFormatterConfig( const std::string& path ) { std::string data; if ( !FileSystem::fileGet( path, data ) ) return; - json j = json::parse( data, nullptr, true, true ); + json j; + + try { + j = json::parse( data, nullptr, true, true ); + } catch ( ... ) { + return; + } if ( j.contains( "config" ) ) { auto& config = j["config"]; @@ -385,6 +391,20 @@ void FormatterPlugin::registerNativeFormatters() { return { false, "", "Couldn't parse CSS file." }; } }; + + mNativeFormatters["json"] = []( const std::string& file ) -> NativeFormatterResult { + std::string data; + if ( !FileSystem::fileGet( file, data ) ) + return { false, "", "Couldn't access JSON file." }; + json j; + try { + j = json::parse( data, nullptr, true, true ); + } catch ( ... ) { + return { false, "", "Error parsing JSON file." }; + } + std::string res( j.dump( 2 ) ); + return { !res.empty(), res, "" }; + }; } } // namespace ecode diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index 2dc1b946d..832bf7218 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -75,7 +75,12 @@ void LinterPlugin::loadLinterConfig( const std::string& path ) { std::string data; if ( !FileSystem::fileGet( path, data ) ) return; - json j = json::parse( data, nullptr, true, true ); + json j; + try { + j = json::parse( data, nullptr, true, true ); + } catch ( ... ) { + return; + } if ( j.contains( "config" ) ) { auto& config = j["config"]; diff --git a/src/tools/ecode/projectdirectorytree.hpp b/src/tools/ecode/projectdirectorytree.hpp index 5b13a75e9..6e3721604 100644 --- a/src/tools/ecode/projectdirectorytree.hpp +++ b/src/tools/ecode/projectdirectorytree.hpp @@ -2,10 +2,13 @@ #define ECODE_PROJECTDIRECTORYTREE_HPP #include "ignorematcher.hpp" +#include #include #include #include #include +#include +#include #include #include #include @@ -14,6 +17,7 @@ using namespace EE; using namespace EE::System; +using namespace EE::UI; using namespace EE::UI::Models; namespace ecode { @@ -21,7 +25,7 @@ namespace ecode { class FileListModel : public Model { public: FileListModel( const std::vector& files, const std::vector& names ) : - mFiles( files ), mNames( names ) {} + mFiles( files ), mNames( names ), mIcons( mNames.size(), nullptr ) {} virtual size_t rowCount( const ModelIndex& ) const { return mNames.size(); } @@ -32,10 +36,18 @@ class FileListModel : public Model { } virtual Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const { - if ( role == ModelRole::Display && index.row() < (Int64)mFiles.size() ) { - return Variant( index.column() == 0 ? mNames[index.row()].c_str() - : mFiles[index.row()].c_str() ); + if ( index.row() >= (Int64)mFiles.size() ) + return {}; + + switch ( role ) { + case ModelRole::Icon: + return Variant( iconFor( index ) ); + default: + case ModelRole::Display: + return Variant( index.column() == 0 ? mNames[index.row()].c_str() + : mFiles[index.row()].c_str() ); } + return {}; } @@ -44,6 +56,22 @@ class FileListModel : public Model { protected: std::vector mFiles; std::vector mNames; + mutable std::vector mIcons; + + UIIcon* iconFor( const ModelIndex& index ) const { + if ( index.column() == 0 ) { + if ( mIcons[index.row()] ) + return mIcons[index.row()]; + auto* scene = SceneManager::instance()->getUISceneNode(); + auto* d = scene->findIcon( + UIIconThemeManager::getIconNameFromFileName( mNames[index.row()], true ) ); + if ( !d ) + d = scene->findIcon( "file" ); + mIcons[index.row()] = d; + return d; + } + return nullptr; + } }; class ProjectDirectoryTree {