From 527c1dc56c91308bbf4ca32ac75ae28ec94f4d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 25 Mar 2026 13:20:10 -0300 Subject: [PATCH] Fix in drop-down height calculation. Added `--diff` parameter. Allow to load multiple files and folders from the CLI. --- include/eepp/ui/tools/uidiffview.hpp | 6 +- src/eepp/ui/tools/uidiffview.cpp | 13 +- src/eepp/ui/uidropdownlist.cpp | 2 +- src/tools/ecode/ecode.cpp | 204 ++++++++++++------ src/tools/ecode/ecode.hpp | 17 +- .../ecode/plugins/debugger/debuggerplugin.cpp | 4 +- src/tools/ecode/plugins/git/gitplugin.cpp | 2 +- .../ecode/plugins/plugincontextprovider.hpp | 2 + 8 files changed, 170 insertions(+), 80 deletions(-) diff --git a/include/eepp/ui/tools/uidiffview.hpp b/include/eepp/ui/tools/uidiffview.hpp index 375f8bcf2..61d498293 100644 --- a/include/eepp/ui/tools/uidiffview.hpp +++ b/include/eepp/ui/tools/uidiffview.hpp @@ -32,7 +32,8 @@ class EE_API UIDiffView : public UIWidget { virtual bool isType( const Uint32& type ) const override; void loadFromPatch( const std::string& patchText, const std::string& originalFilePath = "" ); - void loadFromStrings( const std::string& oldText, const std::string& newText ); + void loadFromStrings( const std::string& oldText, const std::string& newText, + const std::string& originalFilePath = "" ); void loadFromFile( const std::string& oldFilePath, const std::string& newFilePath ); UICodeEditor* getEditor() const { return mEditor; } @@ -110,6 +111,7 @@ class EE_API UIDiffView : public UIWidget { void updateButtonsText(); }; -}}} // namespace EE::UI::Tools +} // namespace Tools +}} // namespace EE::UI #endif // EE_UI_TOOLS_UIDIFFVIEW_HPP diff --git a/src/eepp/ui/tools/uidiffview.cpp b/src/eepp/ui/tools/uidiffview.cpp index 36e831044..aa555d1cf 100644 --- a/src/eepp/ui/tools/uidiffview.cpp +++ b/src/eepp/ui/tools/uidiffview.cpp @@ -790,7 +790,8 @@ void UIDiffView::loadFromPatch( const std::string& patchText, onSizeChange(); } -void UIDiffView::loadFromStrings( const std::string& oldText, const std::string& newText ) { +void UIDiffView::loadFromStrings( const std::string& oldText, const std::string& newText, + const std::string& originalFilePath ) { mLines.clear(); std::vector leftLines = String::split( oldText, '\n', true ); @@ -828,9 +829,15 @@ void UIDiffView::loadFromStrings( const std::string& oldText, const std::string& computeSubLineDiff( oldLine, newLine ); } ); - mShowCompleteView = false; - mCompleteViewToggle->setText( i18n( "diffview_complete", "Complete" ) ); + if ( !originalFilePath.empty() ) { + auto def = SyntaxDefinitionManager::instance()->getByExtension( originalFilePath ); + mSyntaxDef = + SyntaxDefinitionManager::instance()->getLanguageDefinition( def.getLanguageIndex() ); + mFileName = FileSystem::fileNameFromPath( originalFilePath ); + } + updateEditorsText(); + updateButtonsText(); onSizeChange(); } diff --git a/src/eepp/ui/uidropdownlist.cpp b/src/eepp/ui/uidropdownlist.cpp index 051254a95..eb0678405 100644 --- a/src/eepp/ui/uidropdownlist.cpp +++ b/src/eepp/ui/uidropdownlist.cpp @@ -114,7 +114,7 @@ UIDropDownList* UIDropDownList::showList() { Float height = std::ceil( std::ceil( eemin( mListBox->getItemsCount(), mStyleConfig.MaxNumVisibleItems ) * - PixelDensity::pxToDp( mListBox->getRowHeight() ) ) + + mListBox->getRowHeight() ) + tPadding.Top + tPadding.Bottom + ( mListBox->getHorizontalScrollBar() && mListBox->getHorizontalScrollBar()->isVisible() && diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 80f9b21b0..17b2fbdb9 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -2696,6 +2696,25 @@ void App::loadDiffFromPath( const std::string& path ) { diffView->setSyntaxColorScheme( *getCurrentColorScheme() ); } +void App::loadDiffFromPaths( const std::string& oldPath, const std::string& newPath ) { + auto diffViewTitle = i18n( "diff_viewer", "Diff Viewer" ); + auto* diffView = Tools::UIDiffView::New(); + auto [tab, iv] = mSplitter->createWidget( diffView, i18n( "diff_viewer", "Diff Viewer" ) ); + if ( !newPath.empty() ) { + std::string fileName = FileSystem::fileNameFromPath( newPath ); + tab->setText( diffViewTitle + ": " + fileName ); + tab->setTooltipText( newPath ); + } else { + tab->setText( diffViewTitle ); + } + auto icon = findIcon( "filetype-diff" ); + tab->setIcon( icon ? icon : findIcon( "file" ) ); + + diffView->setHeadersVisible( true ); + diffView->loadFromFile( oldPath, newPath ); + diffView->setSyntaxColorScheme( *getCurrentColorScheme() ); +} + void App::openFileFromPath( const std::string& path ) { std::string ext = FileSystem::fileExtension( path ); if ( !Image::isImageExtension( path ) && !SoundFileFactory::isKnownFileExtension( path ) && @@ -3637,79 +3656,107 @@ void App::initProjectTreeViewUI() { mProjectTreeView->setAutoExpandOnSingleColumn( true ); } -void App::initProjectTreeView( std::string path, bool openClean ) { +void App::discardEmptyTab() { + // If we opened a new tab and the first tab is simply empty, discard it + if ( mSplitter->getTabWidgets().size() == 1 && + mSplitter->getTabWidgets()[0]->getTabCount() == 2 && + mSplitter->getTabWidgets()[0]->getTab( 0 ) ) { + auto tab = mSplitter->getTabWidgets()[0]->getTab( 0 ); + if ( tab && tab->getOwnedWidget() && tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { + auto editor = tab->getOwnedWidget()->asType(); + if ( editor->hasDocument() && editor->getDocument().isEmpty() ) { + mSplitter->getTabWidgets()[0]->removeTab( tab ); + } + } + } +}; + +void App::initProjectTreeView( std::vector&& paths, bool openClean ) { initProjectTreeViewUI(); - bool hasPosition = pathHasPosition( path ); - TextPosition initialPosition; - if ( hasPosition ) { - auto pathAndPosition = getPathAndPosition( path ); - path = pathAndPosition.first; - initialPosition = pathAndPosition.second; - } + const auto getInitialPosition = []( std::string& path ) -> TextPosition { + bool hasPosition = pathHasPosition( path ); + TextPosition initialPosition; + if ( hasPosition ) { + auto pathAndPosition = getPathAndPosition( path ); + path = pathAndPosition.first; + initialPosition = pathAndPosition.second; + } + return initialPosition; + }; - if ( !path.empty() ) { + for ( auto& path : paths ) path = FileSystem::expandTilde( path ); - if ( FileSystem::isDirectory( path ) ) { - loadFolder( path ); - } else if ( String::startsWith( path, "https://" ) || - String::startsWith( path, "http://" ) ) { - loadFolder( "." ); - loadFileFromPath( path, false ); - } else { - std::string rpath( FileSystem::getRealPath( path ) ); - std::string folderPath( FileSystem::fileRemoveFileName( rpath ) ); + if ( !paths.empty() ) { + bool openedFolder = false; + bool inNewTab = paths.size() != 1; - if ( FileSystem::isDirectory( folderPath ) ) { - loadFileSystemMatcher( folderPath ); + for ( auto& path : paths ) { + if ( FileSystem::isDirectory( path ) ) { + loadFolder( path, openedFolder ); + openedFolder = true; + } else if ( String::startsWith( path, "https://" ) || + String::startsWith( path, "http://" ) ) { + if ( !openedFolder ) + loadFolder( "." ); + loadFileFromPath( path, inNewTab ); + } else { + std::string rpath( FileSystem::getRealPath( paths[0] ) ); + std::string folderPath( FileSystem::fileRemoveFileName( rpath ) ); - mFileSystemModel = - FileSystemModel::New( folderPath, FileSystemModel::Mode::FilesAndDirectories, - { true, - true, - true, - {}, - [this]( const std::string& filePath ) -> bool { - return isFileVisibleInTreeView( filePath ); - } }, - &mUISceneNode->getTranslator(), mThreadPool ); + if ( !inNewTab && FileSystem::isDirectory( folderPath ) ) { + loadFileSystemMatcher( folderPath ); - mProjectTreeView->setModel( mFileSystemModel ); - mProjectViewEmptyCont->setVisible( false ); + mFileSystemModel = FileSystemModel::New( + folderPath, FileSystemModel::Mode::FilesAndDirectories, + { true, + true, + true, + {}, + [this]( const std::string& filePath ) -> bool { + return isFileVisibleInTreeView( filePath ); + } }, + &mUISceneNode->getTranslator(), mThreadPool ); - if ( mFileSystemListener ) - mFileSystemListener->setFileSystemModel( mFileSystemModel ); + mProjectTreeView->setModel( mFileSystemModel ); + mProjectViewEmptyCont->setVisible( false ); - auto forcePosition = getForcePositionFn( initialPosition ); - auto onLoaded = [this, forcePosition]( UICodeEditor* codeEditor, - const std::string& path ) { - if ( forcePosition ) - forcePosition( codeEditor, path ); - syncProjectTreeWithEditor( mSplitter->getCurEditor() ); - }; + if ( mFileSystemListener ) + mFileSystemListener->setFileSystemModel( mFileSystemModel ); - if ( FileSystem::fileExists( rpath ) ) { - loadFileFromPath( rpath, false, nullptr, onLoaded ); - } else if ( FileSystem::fileCanWrite( folderPath ) ) { - loadFileFromPath( rpath, false, nullptr, onLoaded ); - } + auto forcePosition = getForcePositionFn( getInitialPosition( paths[0] ) ); + auto onLoaded = [this, forcePosition]( UICodeEditor* codeEditor, + const std::string& path ) { + if ( forcePosition ) + forcePosition( codeEditor, path ); + syncProjectTreeWithEditor( mSplitter->getCurEditor() ); + }; - // If we opened a new tab and the first tab is simply empty, discard it - if ( mSplitter->getTabWidgets().size() == 1 && - mSplitter->getTabWidgets()[0]->getTabCount() == 2 && - mSplitter->getTabWidgets()[0]->getTab( 0 ) ) { - auto tab = mSplitter->getTabWidgets()[0]->getTab( 0 ); - if ( tab && tab->getOwnedWidget() && - tab->getOwnedWidget()->isType( UI_TYPE_CODEEDITOR ) ) { - auto editor = tab->getOwnedWidget()->asType(); - if ( editor->hasDocument() && editor->getDocument().isEmpty() ) { - mSplitter->getTabWidgets()[0]->removeTab( tab ); - } + if ( FileSystem::fileExists( rpath ) ) { + loadFileFromPath( rpath, false, nullptr, onLoaded ); + } else if ( FileSystem::fileCanWrite( folderPath ) ) { + loadFileFromPath( rpath, false, nullptr, onLoaded ); + } + + discardEmptyTab(); + + mSettings->updateProjectSettingsMenu(); + } else { + auto forcePosition = getForcePositionFn( getInitialPosition( path ) ); + auto onLoaded = [this, forcePosition]( UICodeEditor* codeEditor, + const std::string& path ) { + if ( forcePosition ) + forcePosition( codeEditor, path ); + syncProjectTreeWithEditor( mSplitter->getCurEditor() ); + }; + + if ( FileSystem::fileExists( rpath ) ) { + loadFileFromPath( rpath, inNewTab, nullptr, onLoaded ); + } else if ( FileSystem::fileCanWrite( folderPath ) ) { + loadFileFromPath( rpath, inNewTab, nullptr, onLoaded ); } } - - mSettings->updateProjectSettingsMenu(); } } } else if ( mConfig.workspace.restoreLastSession && !mRecentFolders.empty() && !openClean ) { @@ -4160,7 +4207,8 @@ void App::init( InitParameters& params ) { if ( params.prematureExit ) return; - if ( !params.openClean && needsRedirectToRunningProcess( params.file ) ) + if ( !params.openClean && params.files.size() == 1 && + needsRedirectToRunningProcess( params.files[0] ) ) return; currentDisplay = displayManager->getDisplayIndex( mConfig.windowState.displayIndex < @@ -4793,7 +4841,7 @@ void App::init( InitParameters& params ) { params.file = ""; #endif - if ( params.terminal && params.file.empty() && params.fileToOpen.empty() ) { + if ( params.terminal && params.files.empty() && params.fileToOpen.empty() ) { mTerminalMode = true; mTerminalModeSidePanelWasVisible = mConfig.ui.showSidePanel; mConfig.ui.showSidePanel = false; @@ -4803,8 +4851,11 @@ void App::init( InitParameters& params ) { getSettingsMenu()->updateViewMenu(); initProjectViewEmptyCont(); mTerminalManager->createNewTerminal(); + } else if ( params.diff ) { + loadDiffFromPaths( params.files[0], params.files[1] ); + discardEmptyTab(); } else { - initProjectTreeView( params.file, params.openClean ); + initProjectTreeView( std::move( params.files ), params.openClean ); } mFileToOpen = FileSystem::expandTilde( params.fileToOpen ); @@ -4886,8 +4937,8 @@ using namespace ecode; EE_MAIN_FUNC int main( int argc, char* argv[] ) { args::ArgumentParser parser( "ecode" ); args::HelpFlag help( parser, "help", "Display this help menu", { 'h', '?', "help" } ); - args::Positional fileOrFolderPos( parser, "file_or_folder", - "The file or folder path to open" ); + args::PositionalList fileOrFolderPos( parser, "file_or_folder", + "The file/s or folder/s path to open" ); args::ValueFlag file( parser, "file", "The file path to open. A file path can also contain the line number and column to " @@ -4984,6 +5035,9 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { "the earliest launched instance instead of the most recently launched one.", { "first-instance" } ); + args::Flag diff( parser, "diff ", "Pairs of file paths to diff.", + { "diff" } ); + #ifdef EE_TEXT_SHAPER_ENABLED args::Flag textShaper( parser, "text-shaper", "WARNING: Do not use this option. It will be completely " @@ -5054,7 +5108,7 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { App::InitParameters params; params.logLevel = logLevel.Get(); - params.file = folder ? folder.Get() : fileOrFolderPos.Get(); + params.files = folder ? std::vector{ folder.Get() } : fileOrFolderPos.Get(); params.pidelDensity = pixelDensityConf ? pixelDensityConf.Get() : 0.f; params.colorScheme = prefersColorScheme ? prefersColorScheme.Get() : ""; params.terminal = terminal.Get(); @@ -5073,6 +5127,26 @@ EE_MAIN_FUNC int main( int argc, char* argv[] ) { params.profile = profile.Get(); params.disablePlugins = disablePlugins.Get(); params.redirectToFirstInstance = redirectToFirstInstance.Get(); + params.diff = diff.Get(); + + if ( params.diff ) { + if ( params.files.size() != 2 ) { + Sys::windowAttachConsole(); + std::cout << "error: 2 values required for '--diff ' but " + << params.files.size() << " was provided" << std::endl; + return EXIT_FAILURE; + } + + for ( size_t i = 0; i < params.files.size(); i++ ) { + if ( !FileSystem::fileExists( params.files[i] ) || + FileSystem::isDirectory( params.files[i] ) ) { + Sys::windowAttachConsole(); + std::cout << "error: invalid parameter " << ( i + 1 ) + << " for '--diff ' " << std::endl; + return EXIT_FAILURE; + } + } + } appInstance = eeNew( App, ( jobs, args ) ); appInstance->init( params ); diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index ec4046bef..a79b0b1d5 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -43,24 +43,25 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { struct InitParameters { LogLevel logLevel{ LogLevel::Info }; - std::string file; + std::vector files; Float pidelDensity{ 0.f }; std::string colorScheme; + std::string css; + std::string fileToOpen; + std::string language; + std::string profile; bool terminal{ false }; bool frameBuffer{ false }; bool benchmarkMode{ false }; - std::string css; - std::string fileToOpen; bool stdOutLogs{ false }; bool disableFileLogs{ false }; bool openClean{ false }; bool portable{ false }; - std::string language; bool incognito{ false }; bool prematureExit{ false }; - std::string profile; bool disablePlugins{ false }; bool redirectToFirstInstance{ false }; + bool diff{ false }; }; void init( InitParameters& ); @@ -558,6 +559,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { void loadDiffFromPath( const std::string& path ); + void loadDiffFromPaths( const std::string& oldPath, const std::string& newPath ); + void loadDiffFromMemory( const std::string& content, const std::string& originalFilePath = "" ); void createAndShowRecentFolderPopUpMenu( Node* recentFoldersBut ); @@ -759,7 +762,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { void initProjectTreeViewUI(); - void initProjectTreeView( std::string path, bool openClean ); + void initProjectTreeView( std::vector&& paths, bool openClean ); void initImageView(); @@ -868,6 +871,8 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider { std::string firstInstanceIndicatorPath() const; void tintTitleBar(); + + void discardEmptyTab(); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index cb72f8635..5949e73c1 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -797,9 +797,9 @@ void DebuggerPlugin::buildSidePanelTab() { - + - + diff --git a/src/tools/ecode/plugins/git/gitplugin.cpp b/src/tools/ecode/plugins/git/gitplugin.cpp index 678e1f9f4..34d8597f1 100644 --- a/src/tools/ecode/plugins/git/gitplugin.cpp +++ b/src/tools/ecode/plugins/git/gitplugin.cpp @@ -1459,7 +1459,7 @@ void GitPlugin::buildSidePanelTab() { - + diff --git a/src/tools/ecode/plugins/plugincontextprovider.hpp b/src/tools/ecode/plugins/plugincontextprovider.hpp index 096072a41..9b54b5c95 100644 --- a/src/tools/ecode/plugins/plugincontextprovider.hpp +++ b/src/tools/ecode/plugins/plugincontextprovider.hpp @@ -148,6 +148,8 @@ class PluginContextProvider { virtual void loadImageFromPath( const std::string& path ) = 0; + virtual void loadDiffFromPaths( const std::string& oldPath, const std::string& newPath ) = 0; + virtual void loadDiffFromPath( const std::string& path ) = 0; virtual void loadDiffFromMemory( const std::string& content,