Git status: Detect staged and non-staged for a single file and generate two different statuses.

Generate events for row mouse clicks.
This commit is contained in:
Martín Lucas Golini
2024-01-26 09:46:00 -03:00
parent dd4de3e7b2
commit bed3581364
5 changed files with 113 additions and 78 deletions

View File

@@ -116,6 +116,11 @@ class EE_API UIAbstractTableView : public UIAbstractView {
UITableCell* getCellFromIndex( const ModelIndex& index ) const;
virtual void onOpenModelIndex( const ModelIndex& index, const Event* triggerEvent = nullptr );
virtual void onOpenMenuModelIndex( const ModelIndex& index,
const Event* triggerEvent = nullptr );
protected:
friend class EE::UI::UITableHeaderColumn;
@@ -185,11 +190,6 @@ class EE_API UIAbstractTableView : public UIAbstractView {
virtual void onScrollChange();
virtual void onOpenModelIndex( const ModelIndex& index, const Event* triggerEvent = nullptr );
virtual void onOpenMenuModelIndex( const ModelIndex& index,
const Event* triggerEvent = nullptr );
virtual void onRowCreated( UITableRow* row );
virtual void onSortColumn( const size_t& colIndex );

View File

@@ -29,14 +29,29 @@ class EE_API UITableRow : public UIWidget {
UITableRow() : UIWidget( "table::row" ) {}
static Event::EventType isMouseEvent( Uint32 msg ) {
switch ( msg ) {
case NodeMessage::MouseClick:
return Event::MouseClick;
case NodeMessage::MouseDoubleClick:
return Event::MouseDoubleClick;
case NodeMessage::MouseDown:
return Event::MouseDown;
case NodeMessage::MouseUp:
return Event::MouseUp;
}
return Event::NoEvent;
}
virtual Uint32 onMessage( const NodeMessage* msg ) {
EventDispatcher* eventDispatcher = getEventDispatcher();
Node* mouseDownNode = eventDispatcher->getMouseDownNode();
Node* draggingNode = eventDispatcher->getNodeDragging();
if ( msg->getMsg() == NodeMessage::MouseDown &&
auto eventType = isMouseEvent( msg->getMsg() );
if ( eventType != Event::NoEvent &&
( mouseDownNode == nullptr || mouseDownNode == this || isParentOf( mouseDownNode ) ) &&
draggingNode == nullptr ) {
sendMouseEvent( Event::MouseDown, eventDispatcher->getMousePos(), msg->getFlags() );
sendMouseEvent( eventType, eventDispatcher->getMousePos(), msg->getFlags() );
}
return 0;
}

View File

@@ -100,6 +100,8 @@ class EE_API UITreeView : public UIAbstractTableView {
void updateContentSize();
virtual void onOpenTreeModelIndex( const ModelIndex& index, bool open );
protected:
enum class IterationDecision {
Continue,
@@ -145,8 +147,6 @@ class EE_API UITreeView : public UIAbstractTableView {
virtual Uint32 onKeyDown( const KeyEvent& event );
virtual void onOpenTreeModelIndex( const ModelIndex& index, bool open );
virtual void onSortColumn( const size_t& colIndex );
void setAllExpanded( const ModelIndex& index = {}, bool expanded = true );

View File

@@ -478,82 +478,21 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
static constexpr auto STATUS_CMD = "-c color.status=never status -b -u -s";
Status s;
std::string buf;
if ( EXIT_SUCCESS != git( DIFF_CMD, projectDir, buf ) )
return s;
getSubModules( projectDir );
LuaPattern subModulePattern( "^Entering '(.*)'" );
auto parseNumStat = [&s, &buf, &projectDir, this, &subModulePattern]() {
LuaPattern pattern( "(%d+)%s+(%d+)%s+(.+)" );
std::string subModulePath = "";
readAllLines( buf, [&]( const std::string_view& line ) {
LuaPattern::Range matches[4];
if ( subModulePattern.matches( line.data(), 0, matches, line.size() ) ) {
subModulePath = String::trim(
line.substr( matches[1].start, matches[1].end - matches[1].start ) );
FileSystem::dirAddSlashAtEnd( subModulePath );
} else if ( pattern.matches( line.data(), 0, matches, line.size() ) ) {
auto inserted = line.substr( matches[1].start, matches[1].end - matches[1].start );
auto deleted = line.substr( matches[2].start, matches[2].end - matches[2].start );
auto file = line.substr( matches[3].start, matches[3].end - matches[3].start );
int inserts;
int deletes;
if ( String::fromString( inserts, inserted ) &&
String::fromString( deletes, deleted ) && ( inserts || deletes ) ) {
auto filePath = subModulePath + file;
auto repo = repoName( filePath, projectDir );
auto repoIt = s.files.find( repo );
GitStatusReport status = { GitStatus::NotSet, GitStatusType::Untracked,
GitStatusChar::Untracked };
if ( repoIt != s.files.end() ) {
bool found = false;
for ( auto& fileIt : repoIt->second ) {
if ( fileIt.file == filePath ) {
fileIt.inserts = inserts;
fileIt.deletes = deletes;
found = true;
break;
}
}
if ( !found ) {
s.files[repo].push_back(
{ std::move( filePath ), inserts, deletes, status } );
}
} else {
s.files.insert(
{ repo, { { std::move( filePath ), inserts, deletes, status } } } );
}
s.totalInserts += inserts;
s.totalDeletions += deletes;
}
}
} );
};
parseNumStat();
git( DIFF_STAGED_CMD, projectDir, buf );
parseNumStat();
bool submodules = hasSubmodules( projectDir );
if ( recurseSubmodules && submodules ) {
gitSubmodules( DIFF_CMD, projectDir, buf );
parseNumStat();
gitSubmodules( DIFF_STAGED_CMD, projectDir, buf );
parseNumStat();
}
LuaPattern subModulePattern( "^Entering '(.*)'" );
bool modifiedSubmodule = false;
auto parseStatus = [&s, &buf, &modifiedSubmodule, &projectDir, this, &subModulePattern]() {
std::string subModulePath = "";
LuaPattern pattern( "^([mMARTUD?%s][mMARTUD?%s])%s(.*)" );
size_t changesCount = countLines( buf );
if ( changesCount > 1000 )
return;
readAllLines( buf, [&]( const std::string_view& line ) {
LuaPattern::Range matches[3];
if ( subModulePattern.matches( line.data(), 0, matches, line.size() ) ) {
@@ -576,11 +515,14 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
return;
}
bool isStagedAndModified =
status.type == GitStatusType::Staged && statusStr[1] != ' ';
auto filePath = subModulePath + file;
auto repo = repoName( filePath, projectDir );
auto repoIt = s.files.find( repo );
bool found = false;
if ( repoIt != s.files.end() ) {
bool found = false;
for ( auto& fileIt : repoIt->second ) {
if ( fileIt.file == filePath ) {
fileIt.report = status;
@@ -588,16 +530,23 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
break;
}
}
if ( !found )
}
if ( !found ) {
s.files[repo].push_back( { std::move( filePath ), 0, 0, status } );
if ( isStagedAndModified ) {
status.type = GitStatusType::Changed;
s.files[repo].push_back( { std::move( filePath ), 0, 0, status } );
} else {
s.files.insert( { repo, { { std::move( filePath ), 0, 0, status } } } );
}
}
}
} );
};
git( STATUS_CMD, projectDir, buf );
if ( EXIT_SUCCESS != git( STATUS_CMD, projectDir, buf ) )
return s;
parseStatus();
if ( modifiedSubmodule && recurseSubmodules && submodules ) {
@@ -605,6 +554,70 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
parseStatus();
}
auto parseNumStat = [&s, &buf, &projectDir, this, &subModulePattern]( bool isStaged ) {
LuaPattern pattern( "(%d+)%s+(%d+)%s+(.+)" );
std::string subModulePath = "";
readAllLines( buf, [&]( const std::string_view& line ) {
LuaPattern::Range matches[4];
if ( subModulePattern.matches( line.data(), 0, matches, line.size() ) ) {
subModulePath = String::trim(
line.substr( matches[1].start, matches[1].end - matches[1].start ) );
FileSystem::dirAddSlashAtEnd( subModulePath );
} else if ( pattern.matches( line.data(), 0, matches, line.size() ) ) {
auto inserted = line.substr( matches[1].start, matches[1].end - matches[1].start );
auto deleted = line.substr( matches[2].start, matches[2].end - matches[2].start );
auto file = line.substr( matches[3].start, matches[3].end - matches[3].start );
int inserts;
int deletes;
if ( String::fromString( inserts, inserted ) &&
String::fromString( deletes, deleted ) && ( inserts || deletes ) ) {
auto filePath = subModulePath + file;
auto repo = repoName( filePath, projectDir );
auto repoIt = s.files.find( repo );
GitStatusReport status = { GitStatus::NotSet, GitStatusType::Untracked,
GitStatusChar::Untracked };
bool found = false;
if ( repoIt != s.files.end() ) {
for ( auto& fileIt : repoIt->second ) {
if ( fileIt.file == filePath ) {
if ( isStaged && fileIt.report.type != Git::GitStatusType::Staged )
continue;
if ( !isStaged && fileIt.report.type == Git::GitStatusType::Staged )
continue;
fileIt.inserts = inserts;
fileIt.deletes = deletes;
found = true;
break;
}
}
}
if ( !found ) {
s.files[repo].push_back(
{ std::move( filePath ), inserts, deletes, status } );
}
s.totalInserts += inserts;
s.totalDeletions += deletes;
}
}
} );
};
if ( EXIT_SUCCESS != git( DIFF_CMD, projectDir, buf ) )
return s;
parseNumStat( false );
git( DIFF_STAGED_CMD, projectDir, buf );
parseNumStat( true );
if ( recurseSubmodules && submodules ) {
gitSubmodules( DIFF_CMD, projectDir, buf );
parseNumStat( false );
gitSubmodules( DIFF_STAGED_CMD, projectDir, buf );
parseNumStat( true );
}
for ( auto& [_, repo] : s.files ) {
for ( auto& val : repo ) {
if ( val.report.symbol == GitStatusChar::Added && val.inserts == 0 ) {

View File

@@ -959,6 +959,13 @@ void GitPlugin::buildSidePanelTab() {
mStatusTree->setAutoColumnsWidth( true );
mStatusTree->setHeadersVisible( false );
mStatusTree->setExpandersAsIcons( true );
mStatusTree->on( Event::OnRowCreated, [this]( const Event* event ) {
UITableRow* row = event->asRowCreatedEvent()->getRow();
row->on( Event::MouseUp, [this, row]( const Event* event ) {
if ( event->asMouseEvent()->getFlags() & EE_BUTTON_RMASK )
mStatusTree->onOpenMenuModelIndex( row->getCurIndex(), event );
} );
} );
mStatusTree->on( Event::OnModelEvent, [this]( const Event* event ) {
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
if ( modelEvent->getModel() == nullptr )