diff --git a/include/eepp/core/containers.hpp b/include/eepp/core/containers.hpp index 1b588182c..0f6ac682e 100644 --- a/include/eepp/core/containers.hpp +++ b/include/eepp/core/containers.hpp @@ -14,15 +14,15 @@ namespace EE { template using UnorderedMap = std::unordered_map; -template using UnorderedSet = std::unordered_set; +template using UnorderedSet = std::unordered_set; #else template using UnorderedMap = robin_hood::unordered_flat_map; -template -using UnorderedSet = robin_hood::unordered_flat_set; +template +using UnorderedSet = robin_hood::unordered_flat_set; #endif diff --git a/include/eepp/scene/scenenode.hpp b/include/eepp/scene/scenenode.hpp index c26f587a6..72b9e732d 100644 --- a/include/eepp/scene/scenenode.hpp +++ b/include/eepp/scene/scenenode.hpp @@ -111,7 +111,7 @@ class EE_API SceneNode : public Node { protected: friend class Node; - typedef std::unordered_set CloseList; + typedef UnorderedSet CloseList; EE::Window::Window* mWindow; ActionManager* mActionManager; @@ -132,9 +132,9 @@ class EE_API SceneNode : public Node { Color mHighlightOverColor; Color mHighlightInvalidationColor; Time mElapsed; - std::unordered_set mScheduledUpdate; - std::unordered_set mScheduledUpdateRemove; - std::unordered_set mMouseOverNodes; + UnorderedSet mScheduledUpdate; + UnorderedSet mScheduledUpdateRemove; + UnorderedSet mMouseOverNodes; Float mDPI; virtual void onSizeChange(); diff --git a/include/eepp/ui/doc/syntaxdefinition.hpp b/include/eepp/ui/doc/syntaxdefinition.hpp index 38525fba9..37dce737c 100644 --- a/include/eepp/ui/doc/syntaxdefinition.hpp +++ b/include/eepp/ui/doc/syntaxdefinition.hpp @@ -97,6 +97,10 @@ class EE_API SyntaxDefinition { SyntaxDefinition& setFileTypes( const std::vector& types ); + bool hasExtensionPriority() const; + + void setExtensionPriority( bool hasExtensionPriority ); + protected: std::string mLanguageName; String::HashType mLanguageId; @@ -108,6 +112,7 @@ class EE_API SyntaxDefinition { std::string mLSPName; bool mAutoCloseXMLTags{ false }; bool mVisible{ true }; + bool mHasExtensionPriority{ false }; }; }}} // namespace EE::UI::Doc diff --git a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp index 9e6b74db1..ea4b98b14 100644 --- a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp +++ b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp @@ -22,6 +22,11 @@ class EE_API SyntaxDefinitionManager { const SyntaxDefinition& getPlainStyle() const; + std::vector + languagesThatSupportExtension( std::string extension ) const; + + bool extensionCanRepresentManyLanguages( std::string extension ) const; + const SyntaxDefinition& getByExtension( const std::string& filePath, bool hFileAsCPP = false ) const; @@ -46,6 +51,8 @@ class EE_API SyntaxDefinitionManager { const SyntaxDefinition* getPtrByLanguageName( const std::string& name ) const; + const SyntaxDefinition* getPtrByLSPName( const std::string& name ) const; + const SyntaxDefinition* getPtrByLanguageId( const String::HashType& id ) const; bool loadFromStream( IOStream& stream, std::vector* addedLangs ); diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 8d727946f..fb5f492a5 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -601,7 +601,7 @@ class EE_API TextDocument { FileInfo mFileRealPath; std::vector mLines; TextRanges mSelection; - std::unordered_set mClients; + UnorderedSet mClients; Mutex mClientsMutex; LineEnding mLineEnding{ LineEnding::LF }; std::atomic mLoading{ false }; diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp index 2bd216408..d5aa068ca 100644 --- a/include/eepp/ui/uiscenenode.hpp +++ b/include/eepp/ui/uiscenenode.hpp @@ -186,10 +186,10 @@ class EE_API UISceneNode : public SceneNode { std::vector mFontFaces; KeyBindings mKeyBindings; std::map mKeyBindingCommands; - std::unordered_set mDirtyStyle; - std::unordered_set mDirtyStyleState; - std::unordered_map mDirtyStyleStateCSSAnimations; - std::unordered_set mDirtyLayouts; + UnorderedSet mDirtyStyle; + UnorderedSet mDirtyStyleState; + UnorderedMap mDirtyStyleStateCSSAnimations; + UnorderedSet mDirtyLayouts; std::vector> mTimes; ColorSchemePreference mColorSchemePreference{ ColorSchemePreference::Dark }; Uint32 mMaxInvalidationDepth{ 2 }; diff --git a/include/eepp/ui/uistyle.hpp b/include/eepp/ui/uistyle.hpp index 9fa92947a..8c6a30853 100644 --- a/include/eepp/ui/uistyle.hpp +++ b/include/eepp/ui/uistyle.hpp @@ -73,7 +73,7 @@ class EE_API UIStyle : public UIState { void removeStructurallyVolatileChild( UIWidget* widget ); - std::unordered_set& getStructurallyVolatileChilds(); + UnorderedSet& getStructurallyVolatileChilds(); bool hasProperty( const CSS::PropertyId& propertyId ) const; @@ -88,7 +88,7 @@ class EE_API UIStyle : public UIState { CSS::AnimationsMap mAnimations; std::set mRelatedWidgets; std::set mSubscribedWidgets; - std::unordered_set mStructurallyVolatileChilds; + UnorderedSet mStructurallyVolatileChilds; bool mChangingState; bool mForceReapplyProperties; bool mDisableAnimations; diff --git a/src/eepp/ui/doc/languages/v.cpp b/src/eepp/ui/doc/languages/v.cpp index be76d6f70..5853fdbd7 100644 --- a/src/eepp/ui/doc/languages/v.cpp +++ b/src/eepp/ui/doc/languages/v.cpp @@ -59,7 +59,7 @@ void addV() { "//", {} - } ); + } ).setExtensionPriority( true ); } }}}} // namespace EE::UI::Doc::Language diff --git a/src/eepp/ui/doc/languages/verilog.cpp b/src/eepp/ui/doc/languages/verilog.cpp index 43ac79e99..37d1c9f26 100644 --- a/src/eepp/ui/doc/languages/verilog.cpp +++ b/src/eepp/ui/doc/languages/verilog.cpp @@ -8,7 +8,7 @@ void addVerilog() { SyntaxDefinitionManager::instance()->add( { "Verilog", - { "%.V$", "%.vl$", "%.vh$" }, + { "%.V$", "%.vl$", "%.vh$", "%.v$" }, { { { "\"", "\"", "\\" }, "string" }, { { "-?0x%x+" }, "number" }, diff --git a/src/eepp/ui/doc/syntaxdefinition.cpp b/src/eepp/ui/doc/syntaxdefinition.cpp index 9dd71fb23..12e6917c0 100644 --- a/src/eepp/ui/doc/syntaxdefinition.cpp +++ b/src/eepp/ui/doc/syntaxdefinition.cpp @@ -51,6 +51,14 @@ SyntaxDefinition& SyntaxDefinition::setFileTypes( const std::vector return *this; } +bool SyntaxDefinition::hasExtensionPriority() const { + return mHasExtensionPriority; +} + +void SyntaxDefinition::setExtensionPriority( bool hasExtensionPriority ) { + mHasExtensionPriority = hasExtensionPriority; +} + const std::vector& SyntaxDefinition::getPatterns() const { return mPatterns; } diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 65dff4fb4..f6c5352d2 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -1958,12 +1958,28 @@ std::vector SyntaxDefinitionManager::getExtensionsPatternsSupported const SyntaxDefinition* SyntaxDefinitionManager::getPtrByLanguageName( const std::string& name ) const { - return &getByLanguageName( name ); + for ( const auto& style : mDefinitions ) { + if ( style.getLanguageName() == name ) + return &style; + } + return nullptr; +} + +const SyntaxDefinition* SyntaxDefinitionManager::getPtrByLSPName( const std::string& name ) const { + for ( const auto& style : mDefinitions ) { + if ( style.getLSPName() == name ) + return &style; + } + return nullptr; } const SyntaxDefinition* SyntaxDefinitionManager::getPtrByLanguageId( const String::HashType& id ) const { - return &getByLanguageId( id ); + for ( const auto& style : mDefinitions ) { + if ( style.getLanguageId() == id ) + return &style; + } + return nullptr; } static SyntaxDefinition loadLanguage( const nlohmann::json& json ) { @@ -2145,15 +2161,72 @@ void SyntaxDefinitionManager::loadFromFolder( const std::string& folderPath ) { } } +std::vector +SyntaxDefinitionManager::languagesThatSupportExtension( std::string extension ) const { + std::vector langs; + if ( extension.empty() ) + return {}; + + if ( extension[0] != '.' ) + extension = '.' + extension; + + for ( const auto& style : mDefinitions ) { + for ( const auto& ext : style.getFiles() ) { + if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || + String::endsWith( ext, "$" ) ) { + LuaPattern words( ext ); + int start, end; + if ( words.find( extension, start, end ) ) + langs.push_back( &style ); + } else if ( extension == ext ) { + langs.push_back( &style ); + } + } + } + return langs; +} + +bool SyntaxDefinitionManager::extensionCanRepresentManyLanguages( std::string extension ) const { + if ( extension.empty() ) + return false; + if ( extension[0] != '.' ) + extension = '.' + extension; + + int count = 0; + for ( const auto& style : mDefinitions ) { + for ( const auto& ext : style.getFiles() ) { + if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || + String::endsWith( ext, "$" ) ) { + LuaPattern words( ext ); + int start, end; + if ( words.find( extension, start, end ) ) { + count++; + if ( count > 1 ) + return true; + } + } else if ( extension == ext ) { + count++; + if ( count > 1 ) + return true; + } + } + } + return false; +} + const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::string& filePath, bool hFileAsCPP ) const { std::string extension( FileSystem::fileExtension( filePath ) ); std::string fileName( FileSystem::fileNameFromPath( filePath ) ); + bool extHasMultipleLangs = extensionCanRepresentManyLanguages( extension ); + // Use the filename instead if ( extension.empty() ) extension = FileSystem::fileNameFromPath( filePath ); + const SyntaxDefinition* def = nullptr; + if ( !extension.empty() ) { for ( const auto& style : mDefinitions ) { for ( const auto& ext : style.getFiles() ) { @@ -2164,17 +2237,30 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri if ( words.find( fileName, start, end ) ) { if ( hFileAsCPP && style.getLSPName() == "c" && ext == "%.h$" ) return getByLSPName( "cpp" ); + + if ( extHasMultipleLangs && !style.hasExtensionPriority() ) { + def = &style; + continue; + } + return style; } } else if ( extension == ext ) { if ( hFileAsCPP && style.getLSPName() == "c" && ext == ".h" ) return getByLSPName( "cpp" ); + + if ( extHasMultipleLangs && !style.hasExtensionPriority() ) { + def = &style; + continue; + } + return style; } } } } - return mDefinitions[0]; + + return def != nullptr ? *def : mDefinitions[0]; } const SyntaxDefinition& SyntaxDefinitionManager::getByHeader( const std::string& header, diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index b63b29a03..e67efc710 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -622,6 +622,7 @@ bool TextDocument::save( IOStream& stream, bool keepUndoRedoStatus ) { cleanChangeId(); mHash = MD5::result( md5Ctx ).digest; + mDirtyOnFileSystem = false; return true; } @@ -2043,8 +2044,10 @@ const SyntaxDefinition& TextDocument::getSyntaxDefinition() const { } void TextDocument::setSyntaxDefinition( const SyntaxDefinition& definition ) { - mSyntaxDefinition = definition; - notifySyntaxDefinitionChange(); + if ( &mSyntaxDefinition != &definition ) { + mSyntaxDefinition = definition; + notifySyntaxDefinitionChange(); + } } Uint64 TextDocument::getCurrentChangeId() const { diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 100e56774..33f2a1ea4 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -681,7 +681,7 @@ void UISceneNode::invalidateStyle( UIWidget* node ) { } } - std::vector::iterator> itEraseList; + std::vector::iterator> itEraseList; for ( auto it = mDirtyStyle.begin(); it != mDirtyStyle.end(); ++it ) { itNode = *it; @@ -715,7 +715,7 @@ void UISceneNode::invalidateStyleState( UIWidget* node, bool disableCSSAnimation } } - std::vector::iterator> itEraseList; + std::vector::iterator> itEraseList; for ( auto it = mDirtyStyleState.begin(); it != mDirtyStyleState.end(); ++it ) { itNode = *it; @@ -754,7 +754,7 @@ void UISceneNode::invalidateLayout( UILayout* node ) { } } - std::vector::iterator> itEraseList; + std::vector::iterator> itEraseList; for ( auto it = mDirtyLayouts.begin(); it != mDirtyLayouts.end(); ++it ) { itNode = *it; diff --git a/src/eepp/ui/uistyle.cpp b/src/eepp/ui/uistyle.cpp index 7c707b96c..199ff3a1a 100644 --- a/src/eepp/ui/uistyle.cpp +++ b/src/eepp/ui/uistyle.cpp @@ -179,7 +179,7 @@ void UIStyle::removeStructurallyVolatileChild( UIWidget* widget ) { mStructurallyVolatileChilds.erase( widget ); } -std::unordered_set& UIStyle::getStructurallyVolatileChilds() { +UnorderedSet& UIStyle::getStructurallyVolatileChilds() { return mStructurallyVolatileChilds; } diff --git a/src/tools/ecode/appconfig.cpp b/src/tools/ecode/appconfig.cpp index 0f886661f..7dbc44513 100644 --- a/src/tools/ecode/appconfig.cpp +++ b/src/tools/ecode/appconfig.cpp @@ -165,6 +165,8 @@ void AppConfig::load( const std::string& confPath, std::string& keybindingsPath, "autoformatter" == creator.first || "lspclient" == creator.first ); pluginManager->setPluginsEnabled( pluginsEnabled, sync ); + languagesExtensions.priorities = ini.getKeyMap( "languages_extensions" ); + iniInfo = FileInfo( ini.path() ); } @@ -278,6 +280,9 @@ void AppConfig::save( const std::vector& recentFiles, for ( const auto& plugin : pluginsEnabled ) ini.setValueB( "plugins", plugin.first, plugin.second ); + for ( const auto& langExt : languagesExtensions.priorities ) + ini.setValue( "languages_extensions", langExt.first, langExt.second ); + ini.writeFile(); iniState.writeFile(); } diff --git a/src/tools/ecode/appconfig.hpp b/src/tools/ecode/appconfig.hpp index f169c9cf4..d5e5df206 100644 --- a/src/tools/ecode/appconfig.hpp +++ b/src/tools/ecode/appconfig.hpp @@ -154,6 +154,10 @@ struct WorkspaceConfig { bool checkForUpdatesAtStartup{ false }; }; +struct LanguagesExtensions { + std::map priorities; +}; + class AppConfig { public: WindowStateConfig windowState; @@ -161,7 +165,6 @@ class AppConfig { CodeEditorConfig editor; DocumentConfig doc; TerminalConfig term; - std::map pluginsConfig; UIConfig ui; IniFile ini; IniFile iniState; @@ -169,6 +172,7 @@ class AppConfig { SearchBarConfig searchBarConfig; GlobalSearchBarConfig globalSearchBarConfig; WorkspaceConfig workspace; + LanguagesExtensions languagesExtensions; void load( const std::string& confPath, std::string& keybindingsPath, std::string& initColorScheme, std::vector& recentFiles, diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 6061d5e52..464019ed2 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -1655,6 +1655,7 @@ void App::loadFileDelayed() { UITab* tab = mSplitter->tabFromEditor( editor ); if ( tab ) tab->setTabSelected(); + updateEditorTabTitle( editor ); } ); } ); } @@ -2013,7 +2014,7 @@ void App::closeFolder() { mStatusBar->setVisible( false ); } -void App::createDocAlert( UICodeEditor* editor ) { +void App::createDocDirtyAlert( UICodeEditor* editor ) { UILinearLayout* docAlert = editor->findByClass( "doc_alert" ); if ( docAlert ) @@ -2077,6 +2078,61 @@ void App::createDocAlert( UICodeEditor* editor ) { Seconds( 10.f ) ); } +void App::createDocManyLangsAlert( UICodeEditor* editor ) { + UILinearLayout* docAlert = editor->findByClass( "doc_alert_manylangs" ); + + if ( docAlert ) + return; + + auto ext = editor->getDocument().getFileInfo().getExtension(); + auto langs = SyntaxDefinitionManager::instance()->languagesThatSupportExtension( ext ); + + if ( langs.size() <= 1 ) + return; + + const auto msg = R"xml( + + + + + + )xml"; + docAlert = mUISceneNode->loadLayoutFromString( msg, editor )->asType(); + + UIStackLayout* stack = docAlert->findByClass( "languages" ); + + if ( !stack ) { + docAlert->close(); + return; + } + + for ( const auto& lang : langs ) { + UIPushButton* btn = UIPushButton::New(); + btn->setParent( stack ); + btn->setText( lang->getLanguageName() ); + btn->setLayoutMarginRight( PixelDensity::dpToPx( 8 ) ); + btn->onClick( [this, editor, lang, docAlert, ext]( auto ) { + editor->getDocument().setSyntaxDefinition( *lang ); + editor->disableReportSizeChangeToChilds(); + docAlert->close(); + editor->setFocus(); + mConfig.languagesExtensions.priorities[ext] = lang->getLSPName(); + } ); + } + + editor->enableReportSizeChangeToChilds(); + + docAlert->runOnMainThread( + [docAlert, editor] { + editor->disableReportSizeChangeToChilds(); + docAlert->close(); + editor->setFocus(); + }, + Seconds( 30.f ) ); +} + void App::loadImageFromMedium( const std::string& path, bool isMemory ) { UIImage* imageView = mImageLayout->findByType( UI_TYPE_IMAGE ); UILoader* loaderView = mImageLayout->findByType( UI_TYPE_LOADER ); @@ -2294,7 +2350,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { TextDocument* doc = docEvent->getDoc(); if ( doc->getFileInfo() != file ) { if ( doc->isDirty() ) { - editor->runOnMainThread( [&, editor]() { createDocAlert( editor ); } ); + editor->runOnMainThread( [&, editor]() { createDocDirtyAlert( editor ); } ); } else { auto hash = String::hash( docEvent->getDoc()->getFilePath() ); editor->removeActionsByTag( hash ); @@ -2352,6 +2408,19 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { tab->setIcon( icon->getSize( mMenuIconSize ) ); } editor->getDocument().setHAsCpp( mProjectDocConfig.hAsCPP ); + + auto ext = editor->getDocument().getFileInfo().getExtension(); + if ( SyntaxDefinitionManager::instance()->extensionCanRepresentManyLanguages( ext ) ) { + auto hasConfig = mConfig.languagesExtensions.priorities.find( ext ); + const SyntaxDefinition* def = nullptr; + if ( hasConfig != mConfig.languagesExtensions.priorities.end() && + ( def = SyntaxDefinitionManager::instance()->getPtrByLSPName( + hasConfig->second ) ) ) { + editor->getDocument().setSyntaxDefinition( *def ); + } else { + createDocManyLangsAlert( editor ); + } + } }; auto docLoaded = [this, editor, docChanged]( const Event* event ) { diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index 83eefbb89..6663551d1 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -564,7 +564,9 @@ class App : public UICodeEditorSplitter::Client { void removeFolderWatches(); - void createDocAlert( UICodeEditor* editor ); + void createDocDirtyAlert( UICodeEditor* editor ); + + void createDocManyLangsAlert( UICodeEditor* editor ); void syncProjectTreeWithEditor( UICodeEditor* editor ); diff --git a/src/tools/ecode/filesystemlistener.cpp b/src/tools/ecode/filesystemlistener.cpp index b97bda0d8..139342a69 100644 --- a/src/tools/ecode/filesystemlistener.cpp +++ b/src/tools/ecode/filesystemlistener.cpp @@ -142,8 +142,16 @@ void FileSystemListener::notifyChange( const FileInfo& file ) { file.getModificationTime() != doc.getFileInfo().getModificationTime() && !doc.isSaving() ) { MD5::Digest curHash = MD5::fromFile( file.getFilepath() ).digest; - if ( curHash != doc.getHash() ) + if ( curHash != doc.getHash() ) { + Log::notice( "Document: \"%s\" has changed on the file system:", + file.getFilepath().c_str() ); + Log::notice( "Modification time on file system: %ul vs %ul in memory", + file.getModificationTime(), doc.getFileInfo().getModificationTime() ); + Log::notice( "Hash on file system: %s vs %s in memory", + MD5::hexDigest( curHash ).c_str(), + MD5::hexDigest( doc.getHash() ).c_str() ); doc.setDirtyOnFileSystem( true ); + } } } ); }