More Git plugin WIP. Added commit.

This commit is contained in:
Martín Lucas Golini
2024-01-23 20:36:01 -03:00
parent a11b6ef9c5
commit bdd018feec
14 changed files with 349 additions and 127 deletions
+17 -15
View File
@@ -1124,21 +1124,23 @@ static void addCMake() {
for ( const auto& keyword : cmake_literals )
cmake_symbols[keyword] = "literal";
SyntaxDefinitionManager::instance()->add( { "CMake",
{ "%.cmake$", "CMakeLists.txt$" },
{
{ { "#", "[^\\]\n" }, "comment" },
{ { "\"", "\"", "\\" }, "string" },
{ { "'", "'", "\\" }, "string" },
{ { "[%a_][%w_]*%s?%f[(]" }, "function" },
{ { "CMAKE_[%w%d_]+" }, "keyword" },
{ { "CTEST_[%w%d_]+" }, "keyword" },
{ { "%u[%u%d_]*_[%u%d_]+" }, "keyword" },
{ { "%${[%a_][%w_]*%}" }, "keyword2" },
{ { "[%a_][%w_]*" }, "symbol" },
},
std::move( cmake_symbols ),
"//" } );
SyntaxDefinitionManager::instance()->add(
{ "CMake",
{ "%.cmake$", "CMakeLists.txt$" },
{
{ { "#", "[^\\]\n" }, "comment" },
{ { "\"", "\"", "\\" }, "string" },
{ { "'", "'", "\\" }, "string" },
{ { "[%a_][%w_]*%s?%f[(]" }, "function" },
{ { "CMAKE_[%w%d_]+" }, "keyword" },
{ { "CTEST_[%w%d_]+" }, "keyword" },
{ { "%u[%u%d_]*_[%u%d_]+" }, "keyword" },
{ { "%${[%a_][%w_]*%}" }, "keyword2" },
{ { "[%a_][%w_]*" }, "symbol" },
},
std::move( cmake_symbols ),
"//",
{ "^cmake_minimum_required.*%c" } } );
}
static void addJSX() {
+17 -1
View File
@@ -1,7 +1,12 @@
#include <eepp/ui/uilayout.hpp>
#include <eepp/ui/uilinearlayout.hpp>
#include <eepp/ui/uimessagebox.hpp>
#include <eepp/ui/uipushbutton.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uistyle.hpp>
#include <eepp/ui/uitextedit.hpp>
#include <eepp/ui/uitextinput.hpp>
#include <eepp/ui/uitextview.hpp>
#include <eepp/ui/uitheme.hpp>
namespace EE { namespace UI {
@@ -12,7 +17,7 @@ UIMessageBox* UIMessageBox::New( const Type& type, const String& message,
}
UIMessageBox::UIMessageBox( const Type& type, const String& message, const Uint32& windowFlags ) :
UIWindow(), mMsgBoxType( type ), mTextInput( NULL ), mCloseShortcut( KEY_UNKNOWN ) {
UIWindow(), mMsgBoxType( type ), mCloseShortcut( KEY_UNKNOWN ) {
mVisible = false;
mStyleConfig.WinFlags = windowFlags;
@@ -41,6 +46,12 @@ UIMessageBox::UIMessageBox( const Type& type, const String& message, const Uint3
->setParent( vlay )
->addEventListener( Event::OnPressEnter,
[this]( const Event* ) { sendCommonEvent( Event::OnConfirm ); } );
} else if ( mMsgBoxType == TEXT_EDIT ) {
mTextEdit = UITextEdit::New();
mTextEdit->setLayoutSizePolicy( SizePolicy::Fixed, SizePolicy::Fixed )
->setLayoutMargin( Rectf( 0, 4, 0, 4 ) )
->setSize( PixelDensity::dpToPx( Vector2f{ 400, 100 } ) )
->setParent( vlay );
}
UILinearLayout* hlay = UILinearLayout::NewHorizontal();
@@ -58,6 +69,7 @@ UIMessageBox::UIMessageBox( const Type& type, const String& message, const Uint3
switch ( mMsgBoxType ) {
case UIMessageBox::INPUT:
case UIMessageBox::TEXT_EDIT:
case UIMessageBox::OK_CANCEL: {
mButtonOK->setText( getTranslatorString( "@string/msg_box_ok", "Ok" ) );
mButtonCancel->setText( getTranslatorString( "@string/msg_box_cancel", "Cancel" ) );
@@ -180,6 +192,10 @@ UITextInput* UIMessageBox::getTextInput() const {
return mTextInput;
}
UITextEdit* UIMessageBox::getTextEdit() const {
return mTextEdit;
}
UILayout* UIMessageBox::getLayoutCont() const {
return mLayoutCont;
}
+1
View File
@@ -2,6 +2,7 @@
#include <eepp/core/string.hpp>
#include <eepp/graphics/fontmanager.hpp>
#include <eepp/graphics/fonttruetype.hpp>
#include <eepp/graphics/text.hpp>
#include <eepp/network/http.hpp>
#include <eepp/network/uri.hpp>
#include <eepp/scene/scenemanager.hpp>
+3
View File
@@ -9,11 +9,14 @@
#include <eepp/scene/scenenode.hpp>
#include <eepp/ui/css/propertydefinition.hpp>
#include <eepp/ui/uilinearlayout.hpp>
#include <eepp/ui/uipushbutton.hpp>
#include <eepp/ui/uirelativelayout.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uistyle.hpp>
#include <eepp/ui/uitextview.hpp>
#include <eepp/ui/uithememanager.hpp>
#include <eepp/ui/uiwindow.hpp>
#define PUGIXML_HEADER_ONLY
#include <pugixml/pugixml.hpp>
@@ -1,5 +1,6 @@
#include <eepp/maps/mapeditor/tilemapproperties.hpp>
#include <eepp/scene/scenemanager.hpp>
#include <eepp/ui/uipushbutton.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uithememanager.hpp>
@@ -1,5 +1,6 @@
#include <eepp/maps/mapeditor/uigotypenew.hpp>
#include <eepp/scene/scenemanager.hpp>
#include <eepp/ui/uipushbutton.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uithememanager.hpp>
@@ -1,5 +1,6 @@
#include <eepp/maps/mapeditor/uimaplayernew.hpp>
#include <eepp/scene/scenemanager.hpp>
#include <eepp/ui/uipushbutton.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uithememanager.hpp>
+1
View File
@@ -206,6 +206,7 @@ void IconManager::init( UISceneNode* sceneNode, FontTrueType* iconFont, FontTrue
{ "repo", 0xea62 },
{ "repo-pull", 0xeb40 },
{ "repo-push", 0xeb41 },
{ "repo-forked", 0xea63 },
{ "tag", 0xea66 } };
for ( const auto& icon : codIcons )
+40 -14
View File
@@ -182,16 +182,30 @@ Git::CheckoutResult Git::checkoutAndCreateLocalBranch( const std::string& remote
return checkout( newBranchName, projectDir );
}
Git::Result Git::add( const std::string& file, const std::string& projectDir ) {
return gitSimple( String::format( "add --force -- \"%s\"", file ), projectDir );
static std::string asList( std::vector<std::string>& files ) {
for ( auto& file : files )
file = "\"" + file + "\"";
return String::join( files );
}
Git::Result Git::add( std::vector<std::string> files, const std::string& projectDir ) {
return gitSimple( String::format( "add --force -- %s", asList( files ) ), projectDir );
}
Git::Result Git::restore( const std::string& file, const std::string& projectDir ) {
return gitSimple( String::format( "restore \"%s\"", file ), projectDir );
}
Git::Result Git::reset( const std::string& file, const std::string& projectDir ) {
return gitSimple( String::format( "reset -q HEAD -- \"%s\"", file ), projectDir );
Git::Result Git::reset( std::vector<std::string> files, const std::string& projectDir ) {
return gitSimple( String::format( "reset -q HEAD -- %s", asList( files ) ), projectDir );
}
Git::Result Git::createBranch( const std::string& branchName, bool _checkout,
const std::string& projectDir ) {
auto res = gitSimple( String::format( "branch --no-track %s", branchName ), projectDir );
if ( _checkout )
checkout( branchName );
return res;
}
Git::Result Git::renameBranch( const std::string& branch, const std::string& newName,
@@ -230,6 +244,12 @@ Git::Result Git::fastForwardMerge( const std::string& projectDir ) {
return gitSimple( "merge --no-commit --ff --ff-only", projectDir );
}
Git::Result Git::updateRef( const std::string& headBranch, const std::string& toCommit,
const std::string& projectDir ) {
return gitSimple( String::format( "update-ref refs/heads/%s %s", headBranch, toCommit ),
projectDir );
}
Git::CountResult Git::branchHistoryPosition( const std::string& localBranch,
const std::string& remoteBranch,
const std::string& projectDir ) {
@@ -345,16 +365,22 @@ static Git::Branch parseTag( std::string_view raw ) {
return newBranch;
}
std::vector<Git::Branch> Git::getAllBranchesAndTags( RefType ref, const std::string& projectDir ) {
std::vector<Git::Branch> Git::getAllBranchesAndTags( RefType ref, std::string_view filterBranch,
const std::string& projectDir ) {
// clang-format off
std::string args( "for-each-ref --format '%(refname) %(refname:short) %(upstream:short) %(objectname) %(upstream:track,nobracket)' --sort=v:refname" );
// clang-format on
if ( ref & RefType::Head )
args.append( " refs/heads" );
if ( ref & RefType::Remote )
args.append( " refs/remotes" );
if ( ref & RefType::Tag )
args.append( " refs/tags" );
if ( filterBranch.empty() ) {
if ( ref & RefType::Head )
args.append( " refs/heads" );
if ( ref & RefType::Remote )
args.append( " refs/remotes" );
if ( ref & RefType::Tag )
args.append( " refs/tags" );
} else {
args.append( " " + filterBranch );
}
std::vector<Branch> branches;
std::string buf;
@@ -406,7 +432,7 @@ bool Git::hasSubmodules( const std::string& projectDir ) {
( !mProjectPath.empty() && FileSystem::fileExists( mProjectPath + ".gitmodules" ) );
}
std::string Git::inSubModule( const std::string& file, const std::string& projectDir ) {
std::string Git::repoName( const std::string& file, const std::string& projectDir ) {
for ( const auto& subRepo : mSubModules ) {
if ( String::startsWith( file, subRepo ) && file.size() != subRepo.size() )
return subRepo;
@@ -454,7 +480,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
if ( String::fromString( inserts, inserted ) &&
String::fromString( deletes, deleted ) && ( inserts || deletes ) ) {
auto filePath = subModulePath + file;
auto repo = inSubModule( filePath, projectDir );
auto repo = repoName( filePath, projectDir );
auto repoIt = s.files.find( repo );
if ( repoIt != s.files.end() ) {
bool found = false;
@@ -660,7 +686,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
modifiedSubmodule = true;
else {
auto filePath = subModulePath + file;
auto repo = inSubModule( filePath, projectDir );
auto repo = repoName( filePath, projectDir );
auto repoIt = s.files.find( repo );
if ( repoIt != s.files.end() ) {
bool found = false;
+15 -8
View File
@@ -201,11 +201,14 @@ class Git {
Status status( bool recurseSubmodules, const std::string& projectDir = "" );
Result add( const std::string& file, const std::string& projectDir = "" );
Result add( std::vector<std::string> files, const std::string& projectDir = "" );
Result restore( const std::string& file, const std::string& projectDir = "" );
Result reset( const std::string& file, const std::string& projectDir = "" );
Result reset( std::vector<std::string> files, const std::string& projectDir = "" );
Result createBranch( const std::string& branchName, bool checkout = false,
const std::string& projectDir = "" );
Result renameBranch( const std::string& branch, const std::string& newName,
const std::string& projectDir = "" );
@@ -218,6 +221,9 @@ class Git {
Result fastForwardMerge( const std::string& projectDir = "" );
Result updateRef( const std::string& headBranch, const std::string& toCommit,
const std::string& projectDir = "" );
CountResult branchHistoryPosition( const std::string& localBranch,
const std::string& remoteBranch,
const std::string& projectDir = "" );
@@ -256,12 +262,19 @@ class Git {
* @brief get all local and remote branches + tags
*/
std::vector<Branch> getAllBranchesAndTags( RefType ref = RefType::All,
std::string_view filterBranch = {},
const std::string& projectDir = "" );
std::vector<std::string> fetchSubModules( const std::string& projectDir );
std::vector<std::string> getSubModules( const std::string& projectDir = "" );
std::string repoName( const std::string& file, const std::string& projectDir = "" );
bool hasSubmodules( const std::string& projectDir );
Result gitSimple( const std::string& cmd, const std::string& projectDir );
protected:
std::string mGitPath;
std::string mProjectPath;
@@ -269,12 +282,6 @@ class Git {
std::string mLastProjectPath;
std::vector<std::string> mSubModules;
bool mSubModulesUpdated{ false };
bool hasSubmodules( const std::string& projectDir );
std::string inSubModule( const std::string& file, const std::string& projectDir );
Result gitSimple( const std::string& cmd, const std::string& projectDir );
};
} // namespace ecode
+205 -55
View File
@@ -9,6 +9,7 @@
#include <eepp/ui/uipopupmenu.hpp>
#include <eepp/ui/uistackwidget.hpp>
#include <eepp/ui/uistyle.hpp>
#include <eepp/ui/uitextedit.hpp>
#include <eepp/ui/uitooltip.hpp>
#include <eepp/ui/uitreeview.hpp>
#include <nlohmann/json.hpp>
@@ -52,7 +53,9 @@ std::string GitPlugin::statusTypeToString( Git::GitStatusType type ) {
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 ) );
hash = hashCombine( hash, String::hash( branch.name ), String::hash( branch.remote ),
String::hash( branch.lastCommit ), branch.type, branch.ahead,
branch.behind );
return hash;
}
@@ -254,9 +257,6 @@ class GitStatusModel : public Model {
rs.type.emplace_back( std::move( rt ) );
}
mStatus.emplace_back( std::move( rs ) );
auto parent = &mStatus[mStatus.size() - 1];
for ( auto& t : parent->type )
t.parent = parent;
}
for ( auto& s : status ) {
@@ -267,6 +267,15 @@ class GitStatusModel : public Model {
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; }
@@ -293,7 +302,8 @@ class GitStatusModel : public Model {
if ( index.internalId() == Status ) {
RepoStatusType* status = reinterpret_cast<RepoStatusType*>( index.internalData() );
size_t f = 0;
for ( size_t i = 0; i < mStatus.size(); i++ ) {
size_t statusSize = mStatus.size();
for ( size_t i = 0; i < statusSize; i++ ) {
if ( &mStatus[i] == status->parent ) {
f = i;
break;
@@ -305,8 +315,10 @@ class GitStatusModel : public Model {
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 < status->parent->type.size(); i++ ) {
for ( size_t i = 0; i < typeSize; i++ ) {
if ( &status->parent->type[i] == status ) {
f = i;
break;
@@ -410,6 +422,18 @@ class GitStatusModel : public Model {
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;
@@ -418,6 +442,23 @@ class GitStatusModel : public Model {
.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 };
@@ -935,12 +976,62 @@ void GitPlugin::fetch() {
runAsync( [this]() { return mGit->fetch(); }, true, true );
}
void GitPlugin::stage( const std::string& file ) {
runAsync( [this, file]() { return mGit->add( file ); }, true, false );
void GitPlugin::branchCreate() {
UIMessageBox* msgBox = UIMessageBox::New(
UIMessageBox::INPUT,
i18n( "git_create_branch_ask",
"Create new branch at current branch (HEAD).\nEnter the name for the branch:" ) );
msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) {
std::string newName( msgBox->getTextInput()->getText().toUtf8() );
if ( newName.empty() )
return;
msgBox->closeWindow();
runAsync( [this, newName]() { return mGit->createBranch( newName, true ); }, false, true );
} );
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
msgBox->setTitle( i18n( "git_add_branch", "Add Branch" ) );
msgBox->center();
msgBox->showWhenReady();
}
void GitPlugin::unstage( const std::string& file ) {
runAsync( [this, file]() { return mGit->reset( file ); }, true, false );
void GitPlugin::commit() {
UIMessageBox* msgBox = UIMessageBox::New( UIMessageBox::TEXT_EDIT,
i18n( "git_commit_message", "Commit Message:" ) );
msgBox->on( Event::OnConfirm, [this, msgBox]( const Event* ) {
std::string msg( msgBox->getTextEdit()->getText().toUtf8() );
if ( msg.empty() )
return;
msgBox->closeWindow();
runAsync( [this, msg]() { return mGit->commit( msg ); }, false, true );
} );
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
msgBox->setTitle( i18n( "git_commit", "Commit" ) );
msgBox->center();
msgBox->showWhenReady();
}
void GitPlugin::stage( const std::vector<std::string>& files ) {
runAsync( [this, files]() { return mGit->add( files ); }, true, false );
}
void GitPlugin::unstage( const std::vector<std::string>& files ) {
runAsync( [this, files]() { return mGit->reset( files ); }, true, false );
}
void GitPlugin::fastForwardMerge( Git::Branch branch ) {
runAsync(
[this, branch]() {
if ( branch.name == mGitBranch )
return mGit->fastForwardMerge();
auto remoteBranch = mGit->getAllBranchesAndTags( Git::RefType::Remote,
"refs/remotes/" + branch.remote );
if ( remoteBranch.empty() )
return Git::Result{ "", -1 };
return mGit->updateRef( branch.name, remoteBranch[0].lastCommit );
},
false, true );
}
void GitPlugin::discard( const std::string& file ) {
@@ -985,7 +1076,9 @@ void GitPlugin::onRegister( UICodeEditor* editor ) {
if ( mTab )
mTab->setTabSelected();
} );
doc.setCommand( "git-pull", [this]() { pull(); } );
doc.setCommand( "git-pull", [this] { pull(); } );
doc.setCommand( "git-fetch", [this] { fetch(); } );
doc.setCommand( "git-commit", [this] { commit(); } );
}
void GitPlugin::onUnregister( UICodeEditor* editor ) {
@@ -1042,10 +1135,10 @@ void GitPlugin::updateBranches() {
if ( !mGit || mGit->getGitFolder().empty() )
return;
auto prevBranch = mGitBranch;
{
Lock l( mGitBranchMutex );
if ( mGitBranch.empty() )
mGitBranch = mGit->branch();
mGitBranch = mGit->branch();
}
auto branches = mGit->getAllBranchesAndTags();
@@ -1053,8 +1146,11 @@ void GitPlugin::updateBranches() {
auto model = GitBranchModel::asModel( std::move( branches ), hash, this );
if ( mBranchesTree &&
static_cast<GitBranchModel*>( mBranchesTree->getModel() )->getHash() == hash )
static_cast<GitBranchModel*>( mBranchesTree->getModel() )->getHash() == hash ) {
if ( prevBranch != mGitBranch )
mBranchesTree->getModel()->invalidate( Model::DontInvalidateIndexes );
return;
}
getUISceneNode()->runOnMainThread( [this, model] { updateBranchesUI( model ); } );
},
@@ -1085,6 +1181,7 @@ void GitPlugin::buildSidePanelTab() {
<hbox lw="mp" lh="wc" margin-bottom="4dp" padding="4dp">
<Widget lw="0" lh="0" lw8="1" />
<PushButton id="branch_pull" text="@string(pull_branch, Pull)" tooltip="@string(pull_branch, Pull Branch)" text-as-fallback="true" icon="icon(repo-pull, 12dp)" margin-left="2dp" />
<PushButton id="branch_push" text="@string(push_branch, Push)" tooltip="@string(push_branch, Push Branch)" text-as-fallback="true" icon="icon(repo-push, 12dp)" margin-left="2dp" />
<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>
-->
@@ -1159,25 +1256,77 @@ void GitPlugin::buildSidePanelTab() {
mStatusTree->setExpandersAsIcons( true );
mStatusTree->on( Event::OnModelEvent, [this]( const Event* event ) {
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
if ( modelEvent->getModel() == nullptr ||
modelEvent->getModelIndex().internalId() != GitStatusModel::GitFile )
return;
auto model = static_cast<const GitStatusModel*>( modelEvent->getModel() );
const Git::DiffFile* file = model->file( modelEvent->getModelIndex() );
if ( file == nullptr )
if ( modelEvent->getModel() == nullptr )
return;
switch ( modelEvent->getModelEventType() ) {
case Abstract::ModelEventType::OpenMenu: {
bool focusOnSelection = mStatusTree->getFocusOnSelection();
mStatusTree->setFocusOnSelection( false );
mStatusTree->getSelection().set( modelEvent->getModelIndex() );
mStatusTree->setFocusOnSelection( focusOnSelection );
openFileStatusMenu( *file );
break;
auto model = static_cast<const GitStatusModel*>( modelEvent->getModel() );
if ( modelEvent->getModelIndex().internalId() == GitStatusModel::GitFile ) {
const Git::DiffFile* file = model->file( modelEvent->getModelIndex() );
if ( file == nullptr )
return;
switch ( modelEvent->getModelEventType() ) {
case Abstract::ModelEventType::OpenMenu: {
bool focusOnSelection = mStatusTree->getFocusOnSelection();
mStatusTree->setFocusOnSelection( false );
mStatusTree->getSelection().set( modelEvent->getModelIndex() );
mStatusTree->setFocusOnSelection( focusOnSelection );
openFileStatusMenu( *file );
break;
}
default:
break;
}
} else if ( modelEvent->getModelIndex().internalId() == GitStatusModel::Status ) {
switch ( modelEvent->getModelEventType() ) {
case Abstract::ModelEventType::OpenMenu: {
bool focusOnSelection = mStatusTree->getFocusOnSelection();
mStatusTree->setFocusOnSelection( false );
mStatusTree->getSelection().set( modelEvent->getModelIndex() );
mStatusTree->setFocusOnSelection( focusOnSelection );
const auto* status = model->statusType( modelEvent->getModelIndex() );
if ( status->type == Git::GitStatusType::Staged ||
status->type == Git::GitStatusType::Untracked ||
status->type == Git::GitStatusType::Changed ) {
UIPopUpMenu* menu = UIPopUpMenu::New();
menu->setId( "git_status_type_menu" );
if ( status->type == Git::GitStatusType::Staged ) {
addMenuItem( menu, "git-commit", "Commit" );
addMenuItem( menu, "git-unstage-all", "Unstage All" );
}
if ( status->type == Git::GitStatusType::Untracked ||
status->type == Git::GitStatusType::Changed )
addMenuItem( menu, "git-stage-all", "Stage All" );
menu->on( Event::OnItemClicked, [this, model]( const Event* event ) {
if ( !mGit )
return;
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
std::string id( item->getId() );
if ( id == "git-commit" ) {
commit();
} else if ( id == "git-stage-all" ) {
stage( model->getFiles( mGit->repoName( "" ),
(Uint32)Git::GitStatusType::Untracked |
(Uint32)Git::GitStatusType::Changed ) );
} else if ( id == "git-unstage-all" ) {
unstage( model->getFiles( mGit->repoName( "" ),
(Uint32)Git::GitStatusType::Staged ) );
}
} );
menu->showOverMouseCursor();
}
break;
}
default:
break;
}
default:
break;
}
} );
}
@@ -1186,27 +1335,24 @@ void GitPlugin::openBranchMenu( const Git::Branch& branch ) {
UIPopUpMenu* menu = UIPopUpMenu::New();
menu->setId( "git_branch_menu" );
auto addFn = [this, menu]( const std::string& txtKey, const std::string& txtVal,
const std::string& icon = "" ) {
menu->add( i18n( txtKey, txtVal ), iconDrawable( icon, 12 ),
KeyBindings::keybindFormat( mKeyBindings[txtKey] ) )
->setId( txtKey );
};
addFn( "git-fetch", "Fetch" );
addMenuItem( menu, "git-fetch", "Fetch" );
addMenuItem( menu, "git-create-branch", "Create Branch", "repo-forked" );
if ( mGitBranch != branch.name ) {
addFn( "git-checkout", "Check Out..." );
addMenuItem( menu, "git-checkout", "Check Out..." );
if ( branch.type == Git::RefType::Head ) {
addFn( "git-branch-rename", "Rename" );
addFn( "git-branch-delete", "Delete" );
addMenuItem( menu, "git-branch-rename", "Rename" );
addMenuItem( menu, "git-branch-delete", "Delete" );
}
} else {
if ( branch.type == Git::RefType::Head ) {
addFn( "git-pull", "Pull", "repo-pull" );
addMenuItem( menu, "git-pull", "Pull", "repo-pull" );
}
}
if ( branch.type == Git::RefType::Head && branch.behind )
addMenuItem( menu, "git-fast-forward-merge", "Fast Forward Merge" );
menu->on( Event::OnItemClicked, [this, branch]( const Event* event ) {
if ( !mGit )
return;
@@ -1222,6 +1368,10 @@ void GitPlugin::openBranchMenu( const Git::Branch& branch ) {
branchRename( branch );
} else if ( id == "git-fetch" ) {
fetch();
} else if ( id == "git-fast-forward-merge" ) {
fastForwardMerge( branch );
} else if ( id == "git-create-branch" ) {
branchCreate();
}
} );
@@ -1232,24 +1382,17 @@ void GitPlugin::openFileStatusMenu( const Git::DiffFile& file ) {
UIPopUpMenu* menu = UIPopUpMenu::New();
menu->setId( "git_file_status_menu" );
auto addFn = [this, menu]( const std::string& txtKey, const std::string& txtVal,
const std::string& icon = "" ) {
menu->add( i18n( txtKey, txtVal ), iconDrawable( icon, 12 ),
KeyBindings::keybindFormat( mKeyBindings[txtKey] ) )
->setId( txtKey );
};
addFn( "git-open-file", "Open File" );
addMenuItem( menu, "git-open-file", "Open File" );
if ( file.statusType != Git::GitStatusType::Staged ) {
addFn( "git-stage", "Stage" );
addMenuItem( menu, "git-stage", "Stage" );
} else {
addFn( "git-unstage", "Unstage" );
addMenuItem( menu, "git-unstage", "Unstage" );
}
menu->addSeparator();
addFn( "git-discard", "Discard" );
addMenuItem( menu, "git-discard", "Discard" );
menu->on( Event::OnItemClicked, [this, file]( const Event* event ) {
if ( !mGit )
@@ -1257,9 +1400,9 @@ void GitPlugin::openFileStatusMenu( const Git::DiffFile& file ) {
UIMenuItem* item = event->getNode()->asType<UIMenuItem>();
std::string id( item->getId() );
if ( id == "git-stage" ) {
stage( file.file );
stage( { file.file } );
} else if ( id == "git-unstage" ) {
unstage( file.file );
unstage( { file.file } );
} else if ( id == "git-discard" ) {
discard( file.file );
} else if ( id == "git-open-file" ) {
@@ -1289,4 +1432,11 @@ void GitPlugin::runAsync( std::function<Git::Result()> fn, bool _updateStatus,
} );
}
void GitPlugin::addMenuItem( UIMenu* menu, const std::string& txtKey, const std::string& txtVal,
const std::string& icon ) {
menu->add( i18n( txtKey, txtVal ), iconDrawable( icon, 12 ),
KeyBindings::keybindFormat( mKeyBindings[txtKey] ) )
->setId( txtKey );
};
} // namespace ecode
+31 -21
View File
@@ -16,6 +16,7 @@ class UITreeView;
class UIDropDownList;
class UIStackWidget;
class UIListBoxItem;
class UIMenu;
} // namespace EE::UI
namespace ecode {
@@ -62,26 +63,8 @@ class GitPlugin : public PluginBase {
std::unique_ptr<Git> mGit;
std::string mGitBranch;
Git::Status mGitStatus;
UILinearLayout* mStatusBar{ nullptr };
UIPushButton* mStatusButton{ nullptr };
Time mRefreshFreq{ Seconds( 5 ) };
GitPlugin( PluginManager* pluginManager, bool sync );
void load( PluginManager* pluginManager );
PluginRequestHandle processMessage( const PluginMessage& msg );
void displayTooltip( UICodeEditor* editor, const Git::Blame& blame, const Vector2f& position );
void hideTooltip( UICodeEditor* editor );
void onRegisterListeners( UICodeEditor*, std::vector<Uint32>& listeners ) override;
void onBeforeUnregister( UICodeEditor* ) override;
void onUnregisterDocument( TextDocument* ) override;
bool mGitFound{ false };
bool mTooltipInfoShowing{ false };
bool mStatusBarDisplayBranch{ true };
@@ -96,6 +79,8 @@ class GitPlugin : public PluginBase {
UITabWidget* mSidePanel{ nullptr };
UITab* mTab{ nullptr };
UILinearLayout* mStatusBar{ nullptr };
UIPushButton* mStatusButton{ nullptr };
UITreeView* mBranchesTree{ nullptr };
UITreeView* mStatusTree{ nullptr };
UIDropDownList* mPanelSwicher{ nullptr };
@@ -116,6 +101,22 @@ class GitPlugin : public PluginBase {
std::optional<CustomTokenizer> mStatusCustomTokenizer;
std::optional<SyntaxDefinition> mTooltipCustomSyntaxDef;
GitPlugin( PluginManager* pluginManager, bool sync );
void load( PluginManager* pluginManager );
PluginRequestHandle processMessage( const PluginMessage& msg );
void displayTooltip( UICodeEditor* editor, const Git::Blame& blame, const Vector2f& position );
void hideTooltip( UICodeEditor* editor );
void onRegisterListeners( UICodeEditor*, std::vector<Uint32>& listeners ) override;
void onBeforeUnregister( UICodeEditor* ) override;
void onUnregisterDocument( TextDocument* ) override;
Color getVarColor( const std::string& var );
void blame( UICodeEditor* editor );
@@ -126,13 +127,19 @@ class GitPlugin : public PluginBase {
void branchDelete( Git::Branch branch );
void fastForwardMerge( Git::Branch branch );
void pull();
void fetch();
void stage( const std::string& file );
void branchCreate();
void unstage( const std::string& file );
void commit();
void stage( const std::vector<std::string>& files );
void unstage( const std::vector<std::string>& files );
void discard( const std::string& file );
@@ -157,6 +164,9 @@ class GitPlugin : public PluginBase {
void openFileStatusMenu( const Git::DiffFile& file );
void runAsync( std::function<Git::Result()> fn, bool updateStatus, bool updateBranches );
void addMenuItem( UIMenu* menu, const std::string& txtKey, const std::string& txtVal,
const std::string& icon = "" );
};
} // namespace ecode