diff --git a/bin/assets/plugins/lspclient.json b/bin/assets/plugins/lspclient.json index 416436ea2..f04e78707 100644 --- a/bin/assets/plugins/lspclient.json +++ b/bin/assets/plugins/lspclient.json @@ -26,9 +26,7 @@ "includeCompletionsWithSnippetText": false, "jsxAttributeCompletionStyle": "auto", "providePrefixAndSuffixTextForRename": true, - "provideRefactorNotApplicableReason": true, - "importModuleSpecifierPreference": "shortest", - "disableSuggestions": false + "provideRefactorNotApplicableReason": true } } }, diff --git a/include/eepp/scene/node.hpp b/include/eepp/scene/node.hpp index c675b8c53..fea8e5621 100644 --- a/include/eepp/scene/node.hpp +++ b/include/eepp/scene/node.hpp @@ -428,6 +428,8 @@ class EE_API Node : public Transformable { Rectf getScreenRect() const; + bool hasEventsOfType( const Uint32& eventType ) const; + protected: typedef std::unordered_map> EventsMap; friend class EventDispatcher; diff --git a/include/eepp/system/md5.hpp b/include/eepp/system/md5.hpp index cec6991d2..5a0427309 100644 --- a/include/eepp/system/md5.hpp +++ b/include/eepp/system/md5.hpp @@ -13,6 +13,10 @@ class EE_API MD5 { std::vector digest; std::string toHexString() { return MD5::hexDigest( digest ); } + + bool operator==( const Result& other ) { return digest == other.digest; } + + bool operator!=( const Result& other ) { return digest != other.digest; } }; /** @return Calculates the md5 hash from a stream */ diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index d820d1d0c..e07ffaf7f 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -1134,6 +1134,10 @@ Uint32 Node::onClick( const std::function& callback, } ); } +bool Node::hasEventsOfType( const Uint32& eventType ) const { + return mEvents.find( eventType ) != mEvents.end(); +} + void Node::removeEventsOfType( const Uint32& eventType ) { auto it = mEvents.find( eventType ); if ( it != mEvents.end() ) diff --git a/src/eepp/system/md5.cpp b/src/eepp/system/md5.cpp index 2692b9430..82a48af75 100644 --- a/src/eepp/system/md5.cpp +++ b/src/eepp/system/md5.cpp @@ -292,6 +292,8 @@ MD5::Result MD5::fromStream( IOStream& stream ) { Int64 size = (Int64)stream.getSize(); char data[512]; + stream.seek( 0 ); + init( &ctx ); while ( size > 0 ) { diff --git a/src/eepp/system/process.cpp b/src/eepp/system/process.cpp index 121f2a412..9233c5170 100644 --- a/src/eepp/system/process.cpp +++ b/src/eepp/system/process.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #if EE_PLATFORM == EE_PLATFORM_MACOSX #define SUBPROCESS_USE_POSIX_SPAWN @@ -53,12 +54,18 @@ bool Process::create( const std::string& command, const Uint32& options, return false; std::vector cmdArr = String::split( command, " ", "", "\"", true ); std::vector strings; - for ( size_t i = 0; i < cmdArr.size(); ++i ) - strings.push_back( cmdArr[i].c_str() ); - strings.push_back( NULL ); mProcess = eeMalloc( sizeof( subprocess_s ) ); memset( mProcess, 0, sizeof( subprocess_s ) ); if ( !environment.empty() ) { + std::string rcommand; + if ( FileSystem::fileExists( command ) ) { + rcommand = command; + } else { + rcommand = Sys::which( command ); + if ( rcommand.empty() ) + return false; + } + strings.push_back( rcommand.c_str() ); std::vector envArr; std::vector envStrings; for ( const auto& pair : environment ) { @@ -73,6 +80,9 @@ bool Process::create( const std::string& command, const Uint32& options, PROCESS_PTR ); return ret; } + for ( size_t i = 0; i < cmdArr.size(); ++i ) + strings.push_back( cmdArr[i].c_str() ); + strings.push_back( NULL ); auto ret = 0 == subprocess_create_ex( strings.data(), options, nullptr, !workingDirectory.empty() ? workingDirectory.c_str() : nullptr, @@ -87,13 +97,22 @@ bool Process::create( const std::string& command, const std::string& args, const return false; std::vector cmdArr = String::split( args, " ", "", "\"", true ); std::vector strings; - strings.push_back( command.c_str() ); - for ( size_t i = 0; i < cmdArr.size(); ++i ) - strings.push_back( cmdArr[i].c_str() ); - strings.push_back( NULL ); mProcess = eeMalloc( sizeof( subprocess_s ) ); memset( mProcess, 0, sizeof( subprocess_s ) ); if ( !environment.empty() ) { + std::string rcommand; + if ( FileSystem::fileExists( command ) ) { + rcommand = command; + } else { + rcommand = Sys::which( command ); + if ( rcommand.empty() ) + return false; + } + strings.push_back( rcommand.c_str() ); + for ( size_t i = 0; i < cmdArr.size(); ++i ) + strings.push_back( cmdArr[i].c_str() ); + strings.push_back( NULL ); + std::vector envArr; std::vector envStrings; for ( const auto& pair : environment ) { @@ -108,6 +127,12 @@ bool Process::create( const std::string& command, const std::string& args, const PROCESS_PTR ); return ret; } + + strings.push_back( command.c_str() ); + for ( size_t i = 0; i < cmdArr.size(); ++i ) + strings.push_back( cmdArr[i].c_str() ); + strings.push_back( NULL ); + auto ret = 0 == subprocess_create_ex( strings.data(), options, nullptr, !workingDirectory.empty() ? workingDirectory.c_str() : nullptr, diff --git a/src/eepp/ui/doc/syntaxtokenizer.cpp b/src/eepp/ui/doc/syntaxtokenizer.cpp index 92b636b62..922f6cd44 100644 --- a/src/eepp/ui/doc/syntaxtokenizer.cpp +++ b/src/eepp/ui/doc/syntaxtokenizer.cpp @@ -176,7 +176,7 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Uint32 curState = SyntaxTokenizer::retrieveSyntaxState( syntax, retState ); }; - size_t size = text.size() - 1; // skip last char ( new line char ) + size_t size = !text.empty() ? text.size() - 1 : 0; // skip last char ( new line char ) while ( i < size ) { if ( curState.currentPatternIdx != SYNTAX_TOKENIZER_STATE_NONE ) { diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 3b88f4bb2..a7ec3e4c8 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -3315,7 +3315,6 @@ TableView#locate_bar_table > tableview::row:selected > tableview::cell:nth-child #status_bar { background-color: var(--list-back); padding-top: 1dp; - padding-bottom: 1dp; } #status_bar > .status_but { padding: 0dp 5dp 0dp 4dp; diff --git a/src/tools/ecode/plugins/formatter/formatterplugin.cpp b/src/tools/ecode/plugins/formatter/formatterplugin.cpp index 3cb46e140..a6a821f46 100644 --- a/src/tools/ecode/plugins/formatter/formatterplugin.cpp +++ b/src/tools/ecode/plugins/formatter/formatterplugin.cpp @@ -1,5 +1,6 @@ #include "formatterplugin.hpp" #include "../../scopedop.hpp" +#include "eepp/system/md5.hpp" #include #include #include @@ -342,7 +343,6 @@ void FormatterPlugin::formatDoc( UICodeEditor* editor ) { } else if ( doc->getFilePath().empty() ) { return; } - IOStreamString fileString; std::string path; if ( doc->isDirty() || !doc->hasFilepath() || formatter.type == FormatterType::Inplace ) { std::string tmpPath; @@ -355,6 +355,7 @@ void FormatterPlugin::formatDoc( UICodeEditor* editor ) { tmpPath = fileDir + "." + String::randString( 8 ) + "." + doc->getFilename(); } + IOStreamString fileString; doc->save( fileString, true ); FileSystem::fileWrite( tmpPath, (Uint8*)fileString.getStreamPointer(), fileString.getSize() ); @@ -364,22 +365,28 @@ void FormatterPlugin::formatDoc( UICodeEditor* editor ) { std::string data; FileSystem::fileGet( tmpPath, data ); - editor->runOnMainThread( [&, data, editor]() { - std::shared_ptr doc = editor->getDocumentRef(); - auto pos = doc->getSelection(); - auto scroll = editor->getScroll(); - doc->selectAll(); - doc->setRunningTransaction( true ); - doc->textInput( data ); - doc->setSelection( pos ); - editor->setScroll( scroll ); - if ( mAutoFormatOnSave ) { - mIsAutoFormatting[doc.get()] = true; - doc->save(); - mIsAutoFormatting[doc.get()] = false; - } - doc->setRunningTransaction( false ); - } ); + auto oldFile = MD5::fromStream( fileString ); + auto newFile = MD5::fromString( data ); + + if ( oldFile != newFile ) { + editor->runOnMainThread( [&, data, editor]() { + std::shared_ptr doc = editor->getDocumentRef(); + auto pos = doc->getSelection(); + auto scroll = editor->getScroll(); + doc->resetCursor(); + doc->selectAll(); + doc->setRunningTransaction( true ); + doc->textInput( data ); + doc->setSelection( pos ); + editor->setScroll( scroll ); + if ( mAutoFormatOnSave ) { + mIsAutoFormatting[doc.get()] = true; + doc->save(); + mIsAutoFormatting[doc.get()] = false; + } + doc->setRunningTransaction( false ); + } ); + } } FileSystem::fileRemove( tmpPath ); @@ -403,6 +410,7 @@ void FormatterPlugin::runFormatter( UICodeEditor* editor, const Formatter& forma std::shared_ptr doc = editor->getDocumentRef(); TextPosition pos = doc->getSelection().start(); auto scroll = editor->getScroll(); + doc->resetCursor(); doc->selectAll(); doc->setRunningTransaction( true ); doc->textInput( res.result ); @@ -440,17 +448,26 @@ void FormatterPlugin::runFormatter( UICodeEditor* editor, const Formatter& forma if ( data.empty() ) return; - editor->runOnMainThread( [&, data, editor]() { - std::shared_ptr doc = editor->getDocumentRef(); - TextPosition pos = doc->getSelection().start(); - auto scroll = editor->getScroll(); - doc->selectAll(); - doc->setRunningTransaction( true ); - doc->textInput( data ); - doc->setSelection( pos ); - editor->setScroll( scroll ); - doc->setRunningTransaction( false ); - } ); + IOStreamString fileString; + editor->getDocumentRef()->save( fileString, true ); + + auto oldFile = MD5::fromStream( fileString ); + auto newFile = MD5::fromString( data ); + + if ( oldFile != newFile ) { + editor->runOnMainThread( [&, data, editor]() { + std::shared_ptr doc = editor->getDocumentRef(); + TextPosition pos = doc->getSelection().start(); + auto scroll = editor->getScroll(); + doc->resetCursor(); + doc->selectAll(); + doc->setRunningTransaction( true ); + doc->textInput( data ); + doc->setSelection( pos ); + editor->setScroll( scroll ); + doc->setRunningTransaction( false ); + } ); + } } } } diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 2a2c3fe59..4a3093e7d 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -330,6 +330,7 @@ bool LSPClientPlugin::processDocumentFormattingResponse( const URI& uri, TextRanges ranges = doc->getSelections(); + doc->resetCursor(); doc->setRunningTransaction( true ); // Sort from bottom to top, this way we don't have to compute any position deltas @@ -1168,8 +1169,12 @@ bool LSPClientPlugin::onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* me menu->addSeparator(); - auto addFn = [this, editor, menu]( const std::string& txtKey, const std::string& txtVal ) { - menu->add( editor->getUISceneNode()->i18n( txtKey, txtVal ), nullptr, + auto addFn = [this, editor, menu]( const std::string& txtKey, const std::string& txtVal, + const std::string& icon = "" ) { + menu->add( editor->getUISceneNode()->i18n( txtKey, txtVal ), + !icon.empty() ? mManager->getUISceneNode()->findIcon( icon )->getSize( + PixelDensity::dpToPxI( 12 ) ) + : nullptr, KeyBindings::keybindFormat( mKeyBindings[txtKey] ) ) ->setId( txtKey ); }; @@ -1196,13 +1201,13 @@ bool LSPClientPlugin::onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* me addFn( "lsp-symbol-references", "Find References to Symbol Under Cursor" ); if ( cap.codeActionProvider ) - addFn( "lsp-symbol-code-action", "Code Action" ); + addFn( "lsp-symbol-code-action", "Code Action", "lightbulb-autofix" ); if ( cap.semanticTokenProvider.full || cap.semanticTokenProvider.fullDelta ) - addFn( "lsp-refresh-semantic-highlighting", "Refresh Semantic Highlighting" ); + addFn( "lsp-refresh-semantic-highlighting", "Refresh Semantic Highlighting", "refresh" ); if ( server->getDefinition().language == "cpp" || server->getDefinition().language == "c" ) - addFn( "lsp-switch-header-source", "Switch Header/Source" ); + addFn( "lsp-switch-header-source", "Switch Header/Source", "filetype-hpp" ); #ifdef EE_DEBUG if ( server->getDefinition().name == "clangd" ) diff --git a/src/tools/ecode/projectbuild.cpp b/src/tools/ecode/projectbuild.cpp index a6a4cf491..c13fc7870 100644 --- a/src/tools/ecode/projectbuild.cpp +++ b/src/tools/ecode/projectbuild.cpp @@ -303,9 +303,6 @@ ProjectBuildManager::~ProjectBuildManager() { ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands( const std::string& buildName, const ProjectBuildi18nFn& i18n, const std::string& buildType ) { - if ( !mLoaded ) - return { i18n( "project_build_not_loaded", "No project build loaded!" ) }; - const auto& buildIt = mBuilds.find( buildName ); if ( buildIt == mBuilds.end() ) @@ -328,7 +325,7 @@ ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands( const std::s auto finalBuild( build.replaceVars( build.mBuild ) ); for ( const auto& step : finalBuild ) { - ProjectBuildCommand buildCmd( step, build.mEnvs ); + ProjectBuildCommand buildCmd( step ); replaceVar( buildCmd, VAR_OS, currentOS ); replaceVar( buildCmd, VAR_NPROC, nproc ); if ( !buildType.empty() ) @@ -337,6 +334,8 @@ ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands( const std::s res.cmds.emplace_back( std::move( buildCmd ) ); } + res.envs = build.mEnvs; + return res; } @@ -363,9 +362,6 @@ ProjectBuildCommandsRes ProjectBuildManager::build( const std::string& buildName ProjectBuildCommandsRes ProjectBuildManager::generateCleanCommands( const std::string& buildName, const ProjectBuildi18nFn& i18n, const std::string& buildType ) { - if ( !mLoaded ) - return { i18n( "project_build_not_loaded", "No project build loaded!" ) }; - const auto& buildIt = mBuilds.find( buildName ); if ( buildIt == mBuilds.end() ) @@ -388,7 +384,7 @@ ProjectBuildCommandsRes ProjectBuildManager::generateCleanCommands( const std::s auto finalBuild( build.replaceVars( build.mClean ) ); for ( const auto& step : finalBuild ) { - ProjectBuildCommand buildCmd( step, build.mEnvs ); + ProjectBuildCommand buildCmd( step ); replaceVar( buildCmd, VAR_OS, currentOS ); replaceVar( buildCmd, VAR_NPROC, nproc ); if ( !buildType.empty() ) @@ -397,6 +393,8 @@ ProjectBuildCommandsRes ProjectBuildManager::generateCleanCommands( const std::s res.cmds.emplace_back( std::move( buildCmd ) ); } + res.envs = build.mEnvs; + return res; } @@ -586,11 +584,14 @@ bool ProjectBuildManager::load() { } bool ProjectBuildManager::save() { - if ( !mLoaded ) - return false; ScopedOp scopedOp( [this]() { mLoading = true; }, [this]() { mLoading = false; } ); json j = ProjectBuild::serialize( mBuilds ); std::string data( j.dump( 2 ) ); + FileInfo file( mProjectFile ); + + if ( !file.exists() && !FileSystem::fileExists( file.getDirectoryPath() ) ) + FileSystem::makeDir( file.getDirectoryPath() ); + if ( !FileSystem::fileWrite( mProjectFile, data ) ) return false; return true; @@ -727,14 +728,14 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str auto options = Process::SearchUserPath | Process::NoWindow | Process::CombinedStdoutStderr; ProjectBuildKeyVal env; if ( !cmd.config.clearSysEnv ) { - if ( !env.empty() ) { + if ( !res.envs.empty() ) { env = getEnvironmentVariables(); - env.insert( env.begin(), cmd.envs.begin(), cmd.envs.end() ); + env.insert( env.begin(), res.envs.begin(), res.envs.end() ); } else { options |= Process::InheritEnvironment; } } else { - env = cmd.envs; + env = res.envs; } if ( !cmd.enabled ) { @@ -878,14 +879,14 @@ void ProjectBuildManager::updateSidePanelTab() { updateBuildType(); - buildList->removeEventsOfType( Event::OnItemSelected ); - buildList->addEventListener( - Event::OnItemSelected, [this, buildEdit, buildList]( const Event* ) { + if ( !buildList->hasEventsOfType( Event::OnItemSelected ) ) { + buildList->on( Event::OnItemSelected, [this, buildEdit, buildList]( const Event* ) { mConfig.buildName = buildList->getListBox()->getItemSelectedText(); mConfig.buildType = ""; buildEdit->setEnabled( true ); updateBuildType(); } ); + } buildButton->setEnabled( !mConfig.buildName.empty() && hasBuild( mConfig.buildName ) && hasBuildCommands( mConfig.buildName ) ); @@ -893,25 +894,34 @@ void ProjectBuildManager::updateSidePanelTab() { cleanButton->setEnabled( !mConfig.buildName.empty() && hasBuild( mConfig.buildName ) && hasCleanCommands( mConfig.buildName ) ); - buildButton->onClick( [this]( auto ) { - if ( isBuilding() ) { - cancelBuild(); - } else { - buildCurrentConfig( mApp->getStatusBuildOutputController() ); - } - } ); + if ( !buildButton->hasEventsOfType( Event::MouseClick ) ) { + buildButton->onClick( [this]( auto ) { + if ( isBuilding() ) { + cancelBuild(); + } else { + buildCurrentConfig( mApp->getStatusBuildOutputController() ); + } + } ); + } - cleanButton->onClick( [this]( auto ) { - if ( isBuilding() ) { - cancelBuild(); - } else { - cleanCurrentConfig( mApp->getStatusBuildOutputController() ); - } - } ); + if ( !cleanButton->hasEventsOfType( Event::MouseClick ) ) { + cleanButton->onClick( [this]( auto ) { + if ( isBuilding() ) { + cancelBuild(); + } else { + cleanCurrentConfig( mApp->getStatusBuildOutputController() ); + } + } ); + } - buildAdd->onClick( [this, buildTab]( auto ) { addBuild( buildTab ); } ); + if ( !buildAdd->hasEventsOfType( Event::MouseClick ) ) { + buildAdd->onClick( [this, buildTab]( auto ) { addBuild( buildTab ); } ); + } - buildEdit->onClick( [this, buildTab]( auto ) { editBuild( mConfig.buildName, buildTab ); } ); + if ( !buildEdit->hasEventsOfType( Event::MouseClick ) ) { + buildEdit->onClick( + [this, buildTab]( auto ) { editBuild( mConfig.buildName, buildTab ); } ); + } } void ProjectBuildManager::updateBuildType() { @@ -941,10 +951,10 @@ void ProjectBuildManager::updateBuildType() { } buildTypeList->setEnabled( !buildTypeList->getListBox()->isEmpty() ); - buildTypeList->removeEventsOfType( Event::OnItemSelected ); - buildTypeList->addEventListener( Event::OnItemSelected, [this, buildTypeList]( const Event* ) { - mConfig.buildType = buildTypeList->getListBox()->getItemSelectedText(); - } ); + if ( !buildTypeList->hasEventsOfType( Event::OnItemSelected ) ) + buildTypeList->on( Event::OnItemSelected, [this, buildTypeList]( const Event* ) { + mConfig.buildType = buildTypeList->getListBox()->getItemSelectedText(); + } ); } std::map ProjectBuildOutputParser::getPresets() { diff --git a/src/tools/ecode/projectbuild.hpp b/src/tools/ecode/projectbuild.hpp index d2b66e1a0..ff1e93999 100644 --- a/src/tools/ecode/projectbuild.hpp +++ b/src/tools/ecode/projectbuild.hpp @@ -203,11 +203,10 @@ class ProjectBuild { }; struct ProjectBuildCommand : public ProjectBuildStep { - ProjectBuildKeyVal envs; ProjectBuildConfig config; - ProjectBuildCommand( const ProjectBuildStep& step, const ProjectBuildKeyVal& envs ) : - ProjectBuildStep( step ), envs( envs ) {} + ProjectBuildCommand( const ProjectBuildStep& step ) : + ProjectBuildStep( step ) {} }; using ProjectBuildCommands = std::vector; @@ -215,6 +214,7 @@ using ProjectBuildCommands = std::vector; struct ProjectBuildCommandsRes { String errorMsg; ProjectBuildCommands cmds; + ProjectBuildKeyVal envs; ProjectBuildCommandsRes() {} diff --git a/src/tools/ecode/uibuildsettings.cpp b/src/tools/ecode/uibuildsettings.cpp index 3b55348ef..7e9c2bcaf 100644 --- a/src/tools/ecode/uibuildsettings.cpp +++ b/src/tools/ecode/uibuildsettings.cpp @@ -226,7 +226,7 @@ class UIBuildStep : public UILinearLayout { mDataBindHolder += UIDataBindBool::New( &mStep->enabled, findByClass( "enabled_checkbox" ) ); - auto placeholder = UIDataBindString::New( &mStep->cmd, findByClass( "input_cmd" ) ); + mDataBindHolder += UIDataBindString::New( &mStep->cmd, findByClass( "input_cmd" ) ); mDataBindHolder += UIDataBindString::New( &mStep->args, findByClass( "input_args" ) ); mDataBindHolder += UIDataBindString::New( &mStep->workingDir, findByClass( "input_working_dir" ) );