mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-02 19:46:29 +03:00
Move git models to its own source files.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
#include "gitplugin.hpp"
|
||||
#include "gitbranchmodel.hpp"
|
||||
#include "gitstatusmodel.hpp"
|
||||
#include <eepp/graphics/primitives.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
@@ -27,14 +29,6 @@ using json = nlohmann::json;
|
||||
|
||||
namespace ecode {
|
||||
|
||||
static const char* GIT_EMPTY = "";
|
||||
static const char* GIT_SUCCESS = "success";
|
||||
static const char* GIT_ERROR = "error";
|
||||
static const char* GIT_BOLD = "bold";
|
||||
static const char* GIT_NOT_BOLD = "notbold";
|
||||
static const char* GIT_TAG = "tag";
|
||||
static const char* GIT_REPO = "repo";
|
||||
|
||||
std::string GitPlugin::statusTypeToString( Git::GitStatusType type ) {
|
||||
switch ( type ) {
|
||||
case Git::GitStatusType::Untracked:
|
||||
@@ -58,420 +52,6 @@ std::vector<std::string> GitPlugin::repos() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t hashBranches( const std::vector<Git::Branch>& branches ) {
|
||||
size_t hash = 0;
|
||||
for ( const auto& branch : branches )
|
||||
hash = hashCombine( hash, String::hash( branch.name ), String::hash( branch.remote ),
|
||||
String::hash( branch.lastCommit ), branch.type, branch.ahead,
|
||||
branch.behind );
|
||||
return hash;
|
||||
}
|
||||
|
||||
class GitBranchModel : public Model {
|
||||
public:
|
||||
static std::shared_ptr<GitBranchModel> asModel( std::vector<Git::Branch>&& branches,
|
||||
size_t hash, GitPlugin* gitPlugin ) {
|
||||
return std::make_shared<GitBranchModel>( std::move( branches ), hash, gitPlugin );
|
||||
}
|
||||
|
||||
enum Column { Name, Remote, Type, LastCommit };
|
||||
|
||||
struct BranchData {
|
||||
std::string branch;
|
||||
std::vector<Git::Branch> data;
|
||||
};
|
||||
|
||||
std::string refTypeToString( Git::RefType type ) {
|
||||
switch ( type ) {
|
||||
case Git::RefType::Head:
|
||||
return mPlugin->i18n( "git_local_branches", "Local Branches" ).toUtf8();
|
||||
case Git::RefType::Remote:
|
||||
return mPlugin->i18n( "git_remote_branches", "Remote Branches" ).toUtf8();
|
||||
case Git::RefType::Tag:
|
||||
return mPlugin->i18n( "git_tags", "Tags" ).toUtf8();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
GitBranchModel( std::vector<Git::Branch>&& branches, size_t hash, GitPlugin* gitPlugin ) :
|
||||
mPlugin( gitPlugin ), mHash( hash ) {
|
||||
std::map<std::string, std::vector<Git::Branch>> branchTypes;
|
||||
for ( auto& branch : branches ) {
|
||||
auto& type = branchTypes[refTypeToString( branch.type )];
|
||||
type.emplace_back( std::move( branch ) );
|
||||
}
|
||||
for ( auto& branch : branchTypes ) {
|
||||
mBranches.emplace_back(
|
||||
BranchData{ std::move( branch.first ), std::move( branch.second ) } );
|
||||
}
|
||||
}
|
||||
|
||||
size_t treeColumn() const { return Column::Name; }
|
||||
|
||||
size_t rowCount( const ModelIndex& index ) const {
|
||||
if ( !index.isValid() )
|
||||
return mBranches.size();
|
||||
if ( index.internalId() == -1 )
|
||||
return mBranches[index.row()].data.size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t columnCount( const ModelIndex& ) const { return 4; }
|
||||
|
||||
ModelIndex parentIndex( const ModelIndex& index ) const {
|
||||
if ( !index.isValid() || index.internalId() == -1 )
|
||||
return {};
|
||||
return createIndex( index.internalId(), index.column(), &mBranches[index.internalId()],
|
||||
-1 );
|
||||
}
|
||||
|
||||
ModelIndex index( int row, int column, const ModelIndex& parent ) const {
|
||||
if ( row < 0 || column < 0 )
|
||||
return {};
|
||||
if ( !parent.isValid() )
|
||||
return createIndex( row, column, &mBranches[row], -1 );
|
||||
if ( parent.internalData() )
|
||||
return createIndex( row, column, &mBranches[parent.row()].data[row], parent.row() );
|
||||
return {};
|
||||
}
|
||||
|
||||
UIIcon* iconFor( const ModelIndex& index ) const {
|
||||
if ( index.column() == (Int64)treeColumn() ) {
|
||||
if ( index.hasParent() ) {
|
||||
Git::Branch* branch = static_cast<Git::Branch*>( index.internalData() );
|
||||
return mPlugin->findIcon( branch->type == Git::RefType::Tag ? GIT_TAG : GIT_REPO );
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Variant data( const ModelIndex& index, ModelRole role ) const {
|
||||
switch ( role ) {
|
||||
case ModelRole::Display: {
|
||||
if ( index.internalId() == -1 ) {
|
||||
if ( index.column() == Column::Name )
|
||||
return Variant( String::format( "%s (%zu)", mBranches[index.row()].branch,
|
||||
mBranches[index.row()].data.size() ) );
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
const Git::Branch& branch = mBranches[index.internalId()].data[index.row()];
|
||||
switch ( index.column() ) {
|
||||
case Column::Name: {
|
||||
if ( branch.type == Git::Remote &&
|
||||
String::startsWith( branch.name, "origin/" ) ) {
|
||||
return Variant( std::string_view{ branch.name }.substr( 7 ).data() );
|
||||
} else if ( branch.type == Git::Head &&
|
||||
( branch.ahead || branch.behind ) ) {
|
||||
if ( branch.ahead && branch.behind ) {
|
||||
return Variant( String::format( "%s (+%ld/-%ld)", branch.name,
|
||||
branch.ahead, branch.behind ) );
|
||||
} else if ( branch.ahead ) {
|
||||
return Variant(
|
||||
String::format( "%s (+%ld)", branch.name, branch.ahead ) );
|
||||
} else {
|
||||
return Variant(
|
||||
String::format( "%s (-%ld)", branch.name, branch.behind ) );
|
||||
}
|
||||
}
|
||||
return Variant( branch.name.c_str() );
|
||||
}
|
||||
case Column::Remote:
|
||||
return Variant( branch.remote.c_str() );
|
||||
case Column::Type:
|
||||
return Variant( branch.typeStr() );
|
||||
case Column::LastCommit:
|
||||
return Variant( branch.lastCommit.c_str() );
|
||||
}
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
case ModelRole::Class: {
|
||||
if ( index.internalId() == -1 )
|
||||
return Variant( GIT_BOLD );
|
||||
const Git::Branch& branch = mBranches[index.internalId()].data[index.row()];
|
||||
if ( branch.name == mPlugin->gitBranch() )
|
||||
return Variant( GIT_BOLD );
|
||||
return Variant( GIT_NOT_BOLD );
|
||||
}
|
||||
case ModelRole::Icon: {
|
||||
return iconFor( index );
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool classModelRoleEnabled() { return true; }
|
||||
|
||||
size_t getHash() const { return mHash; }
|
||||
|
||||
protected:
|
||||
std::vector<BranchData> mBranches;
|
||||
GitPlugin* mPlugin{ nullptr };
|
||||
size_t mHash{ 0 };
|
||||
};
|
||||
|
||||
class GitStatusModel : public Model {
|
||||
public:
|
||||
static std::shared_ptr<GitStatusModel> asModel( Git::FilesStatus status,
|
||||
GitPlugin* gitPlugin ) {
|
||||
return std::make_shared<GitStatusModel>( std::move( status ), gitPlugin );
|
||||
}
|
||||
|
||||
struct RepoStatusType;
|
||||
struct RepoStatus;
|
||||
|
||||
struct DiffFile : Git::DiffFile {
|
||||
DiffFile( Git::DiffFile&& df, RepoStatusType* parent ) :
|
||||
Git::DiffFile( df ), parent( parent ){};
|
||||
RepoStatusType* parent;
|
||||
};
|
||||
|
||||
struct RepoStatusType {
|
||||
std::string typeStr;
|
||||
Git::GitStatusType type;
|
||||
std::vector<DiffFile> files;
|
||||
RepoStatus* parent{ nullptr };
|
||||
};
|
||||
|
||||
struct RepoStatus {
|
||||
std::string repo;
|
||||
std::vector<RepoStatusType> type;
|
||||
};
|
||||
|
||||
enum Column { File, State, Inserted, Removed, RelativeDirectory };
|
||||
|
||||
GitStatusModel( Git::FilesStatus&& status, GitPlugin* gitPlugin ) : mPlugin( gitPlugin ) {
|
||||
std::map<std::string, std::set<Git::GitStatusType>> typesFound;
|
||||
std::unordered_map<std::string, size_t> repoPos;
|
||||
std::unordered_map<size_t, std::unordered_map<Git::GitStatusType, size_t>> repoTypePos;
|
||||
|
||||
for ( auto& s : status )
|
||||
for ( auto& f : s.second )
|
||||
typesFound[s.first].insert( f.report.type );
|
||||
|
||||
for ( const auto& tf : typesFound ) {
|
||||
RepoStatus rs;
|
||||
rs.repo = tf.first;
|
||||
size_t pos = mStatus.size();
|
||||
repoPos[rs.repo] = pos;
|
||||
for ( const auto& s : tf.second ) {
|
||||
RepoStatusType rt;
|
||||
rt.typeStr = mPlugin->statusTypeToString( s );
|
||||
rt.type = s;
|
||||
repoTypePos[pos][s] = rs.type.size();
|
||||
rs.type.emplace_back( std::move( rt ) );
|
||||
}
|
||||
mStatus.emplace_back( std::move( rs ) );
|
||||
}
|
||||
|
||||
for ( auto& s : status ) {
|
||||
for ( auto& fv : s.second ) {
|
||||
auto pos = repoPos[s.first];
|
||||
auto typePos = repoTypePos[pos][fv.report.type];
|
||||
DiffFile df( std::move( fv ), &mStatus[pos].type[typePos] );
|
||||
mStatus[pos].type[typePos].files.emplace_back( std::move( df ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Set the parents after the addreses are stable
|
||||
for ( auto& status : mStatus ) {
|
||||
for ( auto& type : status.type ) {
|
||||
for ( auto& f : type.files )
|
||||
f.parent = &type;
|
||||
type.parent = &status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t treeColumn() const { return Column::File; }
|
||||
|
||||
size_t rowCount( const ModelIndex& index ) const {
|
||||
if ( !index.isValid() )
|
||||
return mStatus.size();
|
||||
|
||||
if ( index.internalId() == Repo )
|
||||
return mStatus[index.row()].type.size();
|
||||
|
||||
if ( index.internalId() == Status )
|
||||
return mStatus[index.parent().row()].type[index.row()].files.size();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t columnCount( const ModelIndex& ) const { return 5; }
|
||||
|
||||
ModelIndex parentIndex( const ModelIndex& index ) const {
|
||||
if ( !index.isValid() || index.internalId() == Repo )
|
||||
return {};
|
||||
|
||||
if ( index.internalId() == Status ) {
|
||||
RepoStatusType* status = reinterpret_cast<RepoStatusType*>( index.internalData() );
|
||||
size_t f = 0;
|
||||
size_t statusSize = mStatus.size();
|
||||
for ( size_t i = 0; i < statusSize; i++ ) {
|
||||
if ( &mStatus[i] == status->parent ) {
|
||||
f = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return createIndex( f, index.column(), status->parent, Repo );
|
||||
}
|
||||
|
||||
if ( index.internalId() == GitFile ) {
|
||||
DiffFile* file = reinterpret_cast<DiffFile*>( index.internalData() );
|
||||
RepoStatusType* status = file->parent;
|
||||
RepoStatus* repoStatus = status->parent;
|
||||
size_t typeSize = repoStatus->type.size();
|
||||
size_t f = 0;
|
||||
for ( size_t i = 0; i < typeSize; i++ ) {
|
||||
if ( &status->parent->type[i] == status ) {
|
||||
f = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return createIndex( f, index.column(), status, Status );
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
enum ModelCategory { Repo, Status, GitFile };
|
||||
|
||||
ModelIndex index( int row, int column, const ModelIndex& parent ) const {
|
||||
if ( row < 0 || column < 0 )
|
||||
return {};
|
||||
|
||||
if ( !parent.isValid() )
|
||||
return createIndex( row, column, &mStatus[row], Repo );
|
||||
|
||||
if ( parent.internalId() == Repo )
|
||||
return createIndex( row, column, &mStatus[parent.row()].type[row], Status );
|
||||
|
||||
if ( parent.internalId() == Status ) {
|
||||
size_t pprow = parent.parent().row();
|
||||
size_t prow = parent.row();
|
||||
return createIndex( row, column, &mStatus[pprow].type[prow].files[row], GitFile );
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Variant data( const ModelIndex& index, ModelRole role ) const {
|
||||
switch ( role ) {
|
||||
case ModelRole::Display: {
|
||||
if ( index.internalId() == Repo ) {
|
||||
if ( index.column() == Column::File )
|
||||
return Variant( mStatus[index.row()].repo.c_str() );
|
||||
return Variant( GIT_EMPTY );
|
||||
} else if ( index.internalId() == Status ) {
|
||||
if ( index.column() == Column::File ) {
|
||||
return Variant(
|
||||
mStatus[index.parent().row()].type[index.row()].typeStr.c_str() );
|
||||
}
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
const Git::DiffFile& s = mStatus[index.parent().parent().row()]
|
||||
.type[index.parent().row()]
|
||||
.files[index.row()];
|
||||
switch ( index.column() ) {
|
||||
case Column::File:
|
||||
return Variant( FileSystem::fileNameFromPath( s.file ) );
|
||||
case Column::Inserted:
|
||||
return Variant( String::format( "+%d ", s.inserts ) );
|
||||
case Column::Removed:
|
||||
return Variant( String::format( "-%d ", s.deletes ) );
|
||||
case Column::State:
|
||||
return Variant( String::format( "%c", s.report.symbol ) );
|
||||
case Column::RelativeDirectory:
|
||||
return Variant( FileSystem::fileRemoveFileName( s.file ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ModelRole::Class: {
|
||||
if ( index.internalId() == GitFile ) {
|
||||
switch ( index.column() ) {
|
||||
case Column::Inserted:
|
||||
return Variant( GIT_SUCCESS );
|
||||
case Column::Removed:
|
||||
return Variant( GIT_ERROR );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ModelRole::Icon: {
|
||||
if ( (Int64)treeColumn() == index.column() ) {
|
||||
if ( index.internalId() == Repo ) {
|
||||
return Variant( mPlugin->findIcon( "repo" ) );
|
||||
} else if ( index.internalId() == GitFile ) {
|
||||
const Git::DiffFile& s = mStatus[index.parent().parent().row()]
|
||||
.type[index.parent().row()]
|
||||
.files[index.row()];
|
||||
std::string iconName =
|
||||
UIIconThemeManager::getIconNameFromFileName( s.file );
|
||||
auto* scene = mPlugin->getUISceneNode();
|
||||
auto* d = scene->findIcon( iconName );
|
||||
if ( !d )
|
||||
return scene->findIcon( "file" );
|
||||
return d;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool classModelRoleEnabled() { return true; }
|
||||
|
||||
const RepoStatus* repo( const ModelIndex& index ) const {
|
||||
if ( index.internalId() != Repo )
|
||||
return nullptr;
|
||||
return &mStatus[index.row()];
|
||||
}
|
||||
|
||||
const RepoStatusType* statusType( const ModelIndex& index ) const {
|
||||
if ( index.internalId() != Status )
|
||||
return nullptr;
|
||||
return &mStatus[index.parent().row()].type[index.row()];
|
||||
}
|
||||
|
||||
const DiffFile* file( const ModelIndex& index ) const {
|
||||
if ( index.internalId() != GitFile )
|
||||
return nullptr;
|
||||
return &mStatus[index.parent().parent().row()]
|
||||
.type[index.parent().row()]
|
||||
.files[index.row()];
|
||||
}
|
||||
|
||||
std::vector<std::string> getFiles( const std::string& repo, uint32_t statusType ) const {
|
||||
std::vector<std::string> files;
|
||||
for ( const auto& status : mStatus ) {
|
||||
if ( status.repo == repo ) {
|
||||
for ( const auto& type : status.type ) {
|
||||
if ( static_cast<uint32_t>( type.type ) & statusType ) {
|
||||
for ( const auto& file : type.files )
|
||||
files.push_back( file.file );
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<RepoStatus> mStatus;
|
||||
GitPlugin* mPlugin{ nullptr };
|
||||
};
|
||||
|
||||
Plugin* GitPlugin::New( PluginManager* pluginManager ) {
|
||||
return eeNew( GitPlugin, ( pluginManager, false ) );
|
||||
}
|
||||
@@ -1238,7 +818,7 @@ void GitPlugin::updateBranches( bool force ) {
|
||||
}
|
||||
|
||||
auto branches = mGit->getAllBranchesAndTags( Git::RefType::All, {}, repoSelected() );
|
||||
auto hash = hashBranches( branches );
|
||||
auto hash = GitBranchModel::hashBranches( branches );
|
||||
auto model = GitBranchModel::asModel( std::move( branches ), hash, this );
|
||||
|
||||
if ( mBranchesTree &&
|
||||
|
||||
Reference in New Issue
Block a user