mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-06-03 03:56:30 +03:00
Git Branches view WIP.
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
#include "gitplugin.hpp"
|
||||
#include "eepp/ui/uistyle.hpp"
|
||||
#include <eepp/graphics/primitives.hpp>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/scopedop.hpp>
|
||||
#include <eepp/ui/doc/syntaxdefinitionmanager.hpp>
|
||||
#include <eepp/ui/uipopupmenu.hpp>
|
||||
#include <eepp/ui/uistyle.hpp>
|
||||
#include <eepp/ui/uitooltip.hpp>
|
||||
#include <eepp/ui/uitreeview.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace EE::UI::Doc;
|
||||
@@ -19,6 +20,208 @@ using json = nlohmann::json;
|
||||
|
||||
namespace ecode {
|
||||
|
||||
static const char* GIT_EMPTY = "";
|
||||
static const char* GIT_SUCCESS = "theme-success";
|
||||
static const char* GIT_ERROR = "theme-error";
|
||||
static const char* GIT_BOLD = "bold";
|
||||
static const char* GIT_NOT_BOLD = "notbold";
|
||||
|
||||
class GitBranchModel : public Model {
|
||||
public:
|
||||
static std::shared_ptr<GitBranchModel> asModel( std::vector<Git::Branch>&& branches,
|
||||
GitPlugin* gitPlugin ) {
|
||||
return std::make_shared<GitBranchModel>( std::move( branches ), 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, GitPlugin* gitPlugin ) :
|
||||
mPlugin( gitPlugin ) {
|
||||
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 {};
|
||||
}
|
||||
|
||||
Variant data( const ModelIndex& index, ModelRole role ) const {
|
||||
switch ( role ) {
|
||||
case ModelRole::Display: {
|
||||
if ( index.internalId() == -1 ) {
|
||||
if ( index.column() == Column::Name )
|
||||
return Variant( mBranches[index.row()].branch.c_str() );
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
const Git::Branch& branch = mBranches[index.internalId()].data[index.row()];
|
||||
switch ( index.column() ) {
|
||||
case Column::Name:
|
||||
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 );
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
virtual bool classModelRoleEnabled() { return true; }
|
||||
|
||||
protected:
|
||||
std::vector<BranchData> mBranches;
|
||||
GitPlugin* mPlugin;
|
||||
};
|
||||
|
||||
class GitStatusModel : public Model {
|
||||
public:
|
||||
static std::shared_ptr<GitStatusModel> asModel( Git::FilesStatus&& status ) {
|
||||
return std::make_shared<GitStatusModel>( std::move( status ) );
|
||||
}
|
||||
|
||||
struct RepoStatus {
|
||||
std::string repo;
|
||||
std::vector<Git::DiffFile> files;
|
||||
};
|
||||
|
||||
enum Column { File, Inserted, Removed, FileStatus };
|
||||
|
||||
GitStatusModel( Git::FilesStatus&& status ) {
|
||||
mStatus.reserve( status.size() );
|
||||
for ( auto& s : status )
|
||||
mStatus.emplace_back( RepoStatus{ std::move( s.first ), std::move( s.second ) } );
|
||||
}
|
||||
|
||||
size_t treeColumn() const { return Column::File; }
|
||||
|
||||
size_t rowCount( const ModelIndex& index ) const {
|
||||
if ( !index.isValid() )
|
||||
return mStatus.size();
|
||||
if ( index.internalId() == -1 )
|
||||
return mStatus[index.row()].files.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(), &mStatus[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, &mStatus[row], -1 );
|
||||
if ( parent.internalData() )
|
||||
return createIndex( row, column, &mStatus[parent.row()].files[row], parent.row() );
|
||||
return {};
|
||||
}
|
||||
|
||||
Variant data( const ModelIndex& index, ModelRole role ) const {
|
||||
if ( role == ModelRole::Display ) {
|
||||
if ( index.internalId() == -1 ) {
|
||||
if ( index.column() == Column::File )
|
||||
return Variant( mStatus[index.row()].repo.c_str() );
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
const Git::DiffFile& s = mStatus[index.internalId()].files[index.row()];
|
||||
switch ( index.column() ) {
|
||||
case Column::File:
|
||||
return Variant( s.file.c_str() );
|
||||
case Column::Inserted:
|
||||
return Variant( s.inserts );
|
||||
case Column::Removed:
|
||||
return Variant( s.deletes );
|
||||
case Column::FileStatus:
|
||||
return Variant( std::string( static_cast<char>( s.status ), 1 ) );
|
||||
}
|
||||
} else if ( role == ModelRole::Class ) {
|
||||
switch ( index.column() ) {
|
||||
case Column::Inserted:
|
||||
return Variant( GIT_SUCCESS );
|
||||
case Column::Removed:
|
||||
return Variant( GIT_ERROR );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
|
||||
virtual bool classModelRoleEnabled() { return true; }
|
||||
|
||||
protected:
|
||||
std::vector<RepoStatus> mStatus;
|
||||
};
|
||||
|
||||
Plugin* GitPlugin::New( PluginManager* pluginManager ) {
|
||||
return eeNew( GitPlugin, ( pluginManager, false ) );
|
||||
}
|
||||
@@ -43,6 +246,8 @@ GitPlugin::~GitPlugin() {
|
||||
mShuttingDown = true;
|
||||
if ( mStatusButton )
|
||||
mStatusButton->close();
|
||||
if ( mSidePanel && mTab )
|
||||
mSidePanel->removeTab( mTab );
|
||||
}
|
||||
|
||||
void GitPlugin::load( PluginManager* pluginManager ) {
|
||||
@@ -137,8 +342,10 @@ void GitPlugin::load( PluginManager* pluginManager ) {
|
||||
mGit = std::make_unique<Git>( pluginManager->getWorkspaceFolder() );
|
||||
mGitFound = !mGit->getGitPath().empty();
|
||||
|
||||
if ( getUISceneNode() )
|
||||
if ( getUISceneNode() ) {
|
||||
updateStatusBar();
|
||||
// updateBranches();
|
||||
}
|
||||
|
||||
subscribeFileSystemListener();
|
||||
mReady = true;
|
||||
@@ -151,6 +358,7 @@ void GitPlugin::updateUINow( bool force ) {
|
||||
return;
|
||||
|
||||
updateStatusBar( force );
|
||||
// updateBranches();
|
||||
}
|
||||
|
||||
void GitPlugin::updateUI() {
|
||||
@@ -275,7 +483,7 @@ void GitPlugin::onFileSystemEvent( const FileEvent& ev, const FileInfo& file ) {
|
||||
return;
|
||||
|
||||
if ( String::startsWith( file.getFilepath(), mGit->getGitFolder() ) &&
|
||||
file.getExtension() == "lock" )
|
||||
( file.getExtension() == "lock" || file.isDirectory() ) )
|
||||
return;
|
||||
|
||||
updateUI();
|
||||
@@ -368,6 +576,10 @@ bool GitPlugin::onMouseLeave( UICodeEditor* editor, const Vector2i&, const Uint3
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GitPlugin::gitBranch() const {
|
||||
return mGitBranch;
|
||||
}
|
||||
|
||||
void GitPlugin::onRegisterListeners( UICodeEditor* editor, std::vector<Uint32>& listeners ) {
|
||||
listeners.push_back(
|
||||
editor->addEventListener( Event::OnCursorPosChange, [this, editor]( const Event* ) {
|
||||
@@ -472,4 +684,56 @@ bool GitPlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void GitPlugin::updateBranches() {
|
||||
if ( !mGit || !mGitFound )
|
||||
return;
|
||||
|
||||
mThreadPool->run( [this] {
|
||||
if ( !mGit )
|
||||
return;
|
||||
|
||||
if ( !mGit->getGitFolder().empty() ) {
|
||||
if ( mGitBranch.empty() )
|
||||
mGitBranch = mGit->branch();
|
||||
auto branches = mGit->getAllBranchesAndTags();
|
||||
auto model = GitBranchModel::asModel( std::move( branches ), this );
|
||||
getUISceneNode()->runOnMainThread( [this, model] { updateSidePanelTab( model ); } );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
void GitPlugin::updateSidePanelTab( std::shared_ptr<GitBranchModel> model ) {
|
||||
buildSidePanelTab();
|
||||
|
||||
UITreeView* tree = mSidePanel->find<UITreeView>( "git_branches_tree" );
|
||||
tree->setModel( model );
|
||||
tree->setColumnsVisible( { GitBranchModel::Name } );
|
||||
}
|
||||
|
||||
void GitPlugin::buildSidePanelTab() {
|
||||
if ( mTab )
|
||||
return;
|
||||
if ( mSidePanel == nullptr )
|
||||
getUISceneNode()->bind( "panel", mSidePanel );
|
||||
UIIcon* icon = getUISceneNode()->findIcon( "source-control" );
|
||||
UIWidget* node = getUISceneNode()->loadLayoutFromString(
|
||||
R"html(
|
||||
<vbox id="git_branches" lw="mp" lh="wc" padding="4dp">
|
||||
<!-- <hbox lw="mp" lh="wc" margin-bottom="4dp">
|
||||
<Input id="branch_filter" lw="0" lw8="1" lh="0" />
|
||||
<PushButton id="branch_add" text="@string(add_branch, Add Branch)" tooltip="@string(add_branch, Add Branch)" text-as-fallback="true" icon="icon(add, 12dp)" margin-left="2dp" />
|
||||
</hbox> -->
|
||||
<TreeView id="git_branches_tree" lw="mp" lh="0" lw8="1" />
|
||||
</vbox>
|
||||
)html" );
|
||||
mTab = mSidePanel->add( getUISceneNode()->i18n( "source_control", "Source Control" ), node,
|
||||
icon ? icon->getSize( PixelDensity::dpToPx( 12 ) ) : nullptr );
|
||||
mTab->setId( "source_control" );
|
||||
mTab->setTextAsFallback( true );
|
||||
|
||||
UITreeView* tree = mSidePanel->find<UITreeView>( "git_branches_tree" );
|
||||
tree->setAutoExpandOnSingleColumn( true );
|
||||
tree->setHeadersVisible( false );
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
Reference in New Issue
Block a user