mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-08-18 06:55:48 +03:00
Remove use of va_list in favor of template fwd args.
Several bug fixes. Git plugin more WIP.
This commit is contained in:
@@ -173,32 +173,31 @@ std::vector<Git::Branch> Git::getAllBranches( const std::string& projectDir ) {
|
||||
|
||||
static Git::Branch parseLocalBranch( const std::string_view& raw ) {
|
||||
static constexpr size_t len = std::string_view{ "refs/heads/"sv }.size();
|
||||
return Git::Branch{ std::string{ raw.substr( len ) }, std::string{}, Git::RefType::Head,
|
||||
std::string{} };
|
||||
if ( len < raw.size() )
|
||||
return Git::Branch{ std::string{ raw.substr( len ) }, std::string{}, Git::RefType::Head,
|
||||
std::string{} };
|
||||
return {};
|
||||
}
|
||||
|
||||
static Git::Branch parseRemoteBranch( const std::string_view& raw ) {
|
||||
static constexpr size_t len = std::string_view( "refs/remotes/"sv ).size();
|
||||
size_t indexOfRemote = raw.find_first_of( '/', len );
|
||||
if ( indexOfRemote != std::string::npos )
|
||||
if ( indexOfRemote != std::string::npos && len < raw.size() ) {
|
||||
return Git::Branch{ std::string{ raw.substr( len ) },
|
||||
std::string{ raw.substr( len, indexOfRemote - len ) },
|
||||
Git::RefType::Remote, std::string{} };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Git::Branch> Git::getAllBranchesAndTags( RefType ref, const std::string& projectDir ) {
|
||||
std::string args( "for-each-ref --format '%(refname)' --sort=-committerdate" );
|
||||
if ( ref & RefType::Head ) {
|
||||
std::string args( "for-each-ref --format '%(refname)' --sort=v:refname" );
|
||||
if ( ref & RefType::Head )
|
||||
args.append( " refs/heads" );
|
||||
}
|
||||
if ( ref & RefType::Remote ) {
|
||||
if ( ref & RefType::Remote )
|
||||
args.append( " refs/remotes" );
|
||||
}
|
||||
if ( ref & RefType::Tag ) {
|
||||
if ( ref & RefType::Tag )
|
||||
args.append( " refs/tags" );
|
||||
args.append( " --sort=-taggerdate" );
|
||||
}
|
||||
|
||||
std::vector<Branch> branches;
|
||||
std::string buf;
|
||||
@@ -206,24 +205,23 @@ std::vector<Git::Branch> Git::getAllBranchesAndTags( RefType ref, const std::str
|
||||
if ( EXIT_SUCCESS != git( args, projectDir, buf ) )
|
||||
return branches;
|
||||
|
||||
branches.reserve( countLines( buf ) );
|
||||
|
||||
readAllLines( buf, [&branches, ref]( const std::string_view& line ) {
|
||||
auto branch = String::trim( line, '\n' );
|
||||
branch = String::trim( line, '\'' );
|
||||
auto branch = String::trim( String::trim( line, '\n' ), '\'' );
|
||||
if ( ( ref & Head ) && String::startsWith( branch, "refs/heads/" ) ) {
|
||||
branches.emplace_back( parseLocalBranch( branch ) );
|
||||
} else if ( ( ref & Remote ) && String::startsWith( branch, "refs/remotes/" ) ) {
|
||||
branches.emplace_back( parseRemoteBranch( branch ) );
|
||||
} else if ( ( ref & Tag ) && String::startsWith( branch, "refs/tags/" ) ) {
|
||||
static constexpr size_t len = std::string_view{ "refs/tags/"sv }.size();
|
||||
branches.push_back( { std::string{ branch.substr( len ) }, std::string{}, RefType::Tag,
|
||||
std::string{} } );
|
||||
Branch newBranch;
|
||||
newBranch.name = branch.substr( len );
|
||||
newBranch.type = RefType::Tag;
|
||||
branches.emplace_back( std::move( newBranch ) );
|
||||
}
|
||||
} );
|
||||
|
||||
std::sort( branches.begin(), branches.end(), []( const Branch& left, const Branch& right ) {
|
||||
return left.type < right.type || left.name < right.name;
|
||||
} );
|
||||
|
||||
return branches;
|
||||
}
|
||||
|
||||
@@ -257,7 +255,7 @@ bool Git::hasSubmodules( const std::string& projectDir ) {
|
||||
|
||||
std::string Git::inSubModule( const std::string& file, const std::string& projectDir ) {
|
||||
for ( const auto& subRepo : mSubModules ) {
|
||||
if ( String::startsWith( file, subRepo ) )
|
||||
if ( String::startsWith( file, subRepo ) && file.size() != subRepo.size() )
|
||||
return subRepo;
|
||||
}
|
||||
return FileSystem::fileNameFromPath( !projectDir.empty() ? projectDir : mProjectPath );
|
||||
@@ -270,26 +268,37 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
std::string buf;
|
||||
if ( EXIT_SUCCESS != git( DIFF_CMD, projectDir, buf ) )
|
||||
return s;
|
||||
auto parseNumStat = [&s, &buf, &projectDir, this]() {
|
||||
|
||||
getSubModules( projectDir );
|
||||
|
||||
LuaPattern subModulePattern( "^Entering '(.*)'" );
|
||||
|
||||
auto parseNumStat = [&s, &buf, &projectDir, this, &subModulePattern]() {
|
||||
auto lastNL = 0;
|
||||
auto nextNL = buf.find_first_of( '\n' );
|
||||
LuaPattern pattern( "(%d+)%s+(%d+)%s+(.+)" );
|
||||
std::string subModulePath = "";
|
||||
while ( nextNL != std::string_view::npos ) {
|
||||
LuaPattern::Range matches[4];
|
||||
if ( pattern.matches( buf.c_str(), lastNL, matches, nextNL ) ) {
|
||||
if ( subModulePattern.matches( buf.c_str(), lastNL, matches, nextNL ) ) {
|
||||
subModulePath = String::trim(
|
||||
buf.substr( matches[1].start, matches[1].end - matches[1].start ) );
|
||||
FileSystem::dirAddSlashAtEnd( subModulePath );
|
||||
} else if ( pattern.matches( buf.c_str(), lastNL, matches, nextNL ) ) {
|
||||
auto inserted = buf.substr( matches[1].start, matches[1].end - matches[1].start );
|
||||
auto deleted = buf.substr( matches[2].start, matches[2].end - matches[2].start );
|
||||
auto file = buf.substr( matches[3].start, matches[3].end - matches[3].start );
|
||||
int inserts;
|
||||
int deletes;
|
||||
if ( String::fromString( inserts, inserted ) &&
|
||||
String::fromString( deletes, deleted ) ) {
|
||||
auto repo = inSubModule( file, projectDir );
|
||||
String::fromString( deletes, deleted ) && ( inserts || deletes ) ) {
|
||||
auto filePath = subModulePath + file;
|
||||
auto repo = inSubModule( filePath, projectDir );
|
||||
auto repoIt = s.files.find( repo );
|
||||
if ( repoIt != s.files.end() ) {
|
||||
bool found = false;
|
||||
for ( auto& fileIt : repoIt->second ) {
|
||||
if ( fileIt.file == file ) {
|
||||
if ( fileIt.file == filePath ) {
|
||||
fileIt.inserts = inserts;
|
||||
fileIt.deletes = deletes;
|
||||
found = true;
|
||||
@@ -297,15 +306,15 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
s.files[repo].push_back( { std::move( file ), inserts, deletes } );
|
||||
s.files[repo].push_back( { std::move( filePath ), inserts, deletes } );
|
||||
} else {
|
||||
s.files.insert( { repo, { { std::move( file ), inserts, deletes } } } );
|
||||
s.files.insert( { repo, { { std::move( filePath ), inserts, deletes } } } );
|
||||
}
|
||||
s.totalInserts += inserts;
|
||||
s.totalDeletions += deletes;
|
||||
}
|
||||
}
|
||||
lastNL = nextNL;
|
||||
lastNL = nextNL + 1;
|
||||
nextNL = buf.find_first_of( '\n', nextNL + 1 );
|
||||
}
|
||||
};
|
||||
@@ -319,19 +328,23 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
parseNumStat();
|
||||
}
|
||||
|
||||
getSubModules( projectDir );
|
||||
|
||||
bool modifiedSubmodule = false;
|
||||
auto parseStatus = [&s, &buf, &modifiedSubmodule, &projectDir, this]() {
|
||||
auto parseStatus = [&s, &buf, &modifiedSubmodule, &projectDir, this, &subModulePattern]() {
|
||||
auto lastNL = 0;
|
||||
auto nextNL = buf.find_first_of( '\n' );
|
||||
LuaPattern pattern( "\n([%sA?][MARTUD?%s])%s(.*)" );
|
||||
LuaPattern pattern( "([mMARTUD?%s][mMARTUD?%s])%s(.*)" );
|
||||
std::string subModulePath = "";
|
||||
while ( nextNL != std::string_view::npos ) {
|
||||
std::string_view line = std::string_view{ buf }.substr( lastNL, nextNL - lastNL );
|
||||
LuaPattern::Range matches[3];
|
||||
if ( pattern.matches( buf.c_str(), lastNL, matches, nextNL ) ) {
|
||||
auto status = buf.substr( matches[1].start, matches[1].end - matches[1].start );
|
||||
String::trimInPlace( status );
|
||||
auto file = buf.substr( matches[2].start, matches[2].end - matches[2].start );
|
||||
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 status = String::trim(
|
||||
line.substr( matches[1].start, matches[1].end - matches[1].start ) );
|
||||
auto file = line.substr( matches[2].start, matches[2].end - matches[2].start );
|
||||
FileStatus rstatus = FileStatus::Unknown;
|
||||
if ( "??" == status )
|
||||
rstatus = FileStatus::Untracked;
|
||||
@@ -354,26 +367,28 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
if ( rstatus == FileStatus::ModifiedSubmodule )
|
||||
modifiedSubmodule = true;
|
||||
else {
|
||||
auto repo = inSubModule( file, projectDir );
|
||||
auto filePath = subModulePath + file;
|
||||
auto repo = inSubModule( filePath, projectDir );
|
||||
auto repoIt = s.files.find( repo );
|
||||
if ( repoIt != s.files.end() ) {
|
||||
bool found = false;
|
||||
for ( auto& fileIt : repoIt->second ) {
|
||||
if ( fileIt.file == file ) {
|
||||
if ( fileIt.file == filePath ) {
|
||||
fileIt.status = rstatus;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found )
|
||||
s.files[repo].push_back( { std::move( file ), 0, 0, rstatus } );
|
||||
s.files[repo].push_back( { std::move( filePath ), 0, 0, rstatus } );
|
||||
} else {
|
||||
s.files.insert( { file, { { file, 0, 0, rstatus } } } );
|
||||
s.files.insert(
|
||||
{ repo, { { std::move( filePath ), 0, 0, rstatus } } } );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastNL = nextNL;
|
||||
lastNL = nextNL + 1;
|
||||
nextNL = buf.find_first_of( '\n', nextNL + 1 );
|
||||
}
|
||||
};
|
||||
@@ -381,7 +396,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
git( STATUS_CMD, projectDir, buf );
|
||||
parseStatus();
|
||||
|
||||
if ( modifiedSubmodule && submodules ) {
|
||||
if ( modifiedSubmodule && recurseSubmodules && submodules ) {
|
||||
gitSubmodules( STATUS_CMD, projectDir, buf );
|
||||
parseStatus();
|
||||
}
|
||||
@@ -393,6 +408,7 @@ Git::Status Git::status( bool recurseSubmodules, const std::string& projectDir )
|
||||
FileSystem::fileGet( ( projectDir.empty() ? mProjectPath : projectDir ) + val.file,
|
||||
fileText );
|
||||
val.inserts = countLines( fileText );
|
||||
s.totalInserts += val.inserts;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ class Git {
|
||||
UpdatedUnmerged = 'U',
|
||||
Deleted = 'D',
|
||||
Untracked = '?',
|
||||
Ignored = 'I',
|
||||
ModifiedSubmodule = 'm',
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ static const std::string GIT_REPO = "repo";
|
||||
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.lastCommit ) );
|
||||
hash = hashCombine( hash, String::hash( branch.name ) );
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -231,11 +231,11 @@ class GitStatusModel : public Model {
|
||||
case Column::File:
|
||||
return Variant( FileSystem::fileNameFromPath( s.file ) );
|
||||
case Column::Inserted:
|
||||
return Variant( String::format( " +%d ", s.inserts ) );
|
||||
return Variant( String::format( "+%d ", s.inserts ) );
|
||||
case Column::Removed:
|
||||
return Variant( String::format( " -%d ", s.deletes ) );
|
||||
return Variant( String::format( "-%d ", s.deletes ) );
|
||||
case Column::State:
|
||||
return Variant( String::format( " %c ", s.status ) );
|
||||
return Variant( String::format( "%c", s.status ) );
|
||||
case Column::RelativeDirectory:
|
||||
return Variant( FileSystem::fileRemoveFileName( s.file ) );
|
||||
}
|
||||
@@ -423,6 +423,7 @@ void GitPlugin::updateStatusBarSync() {
|
||||
->setEnabled( !mGitContentView->isEnabled() );
|
||||
|
||||
if ( !mGit->getGitFolder().empty() ) {
|
||||
Lock l( mGitStatusMutex );
|
||||
mStatusTree->setModel( GitStatusModel::asModel( mGitStatus.files, this ) );
|
||||
mStatusTree->expandAll();
|
||||
}
|
||||
@@ -459,12 +460,15 @@ void GitPlugin::updateStatusBarSync() {
|
||||
if ( mGit->getGitFolder().empty() )
|
||||
return;
|
||||
|
||||
std::string text( mStatusBarDisplayModifications &&
|
||||
( mGitStatus.totalInserts || mGitStatus.totalDeletions )
|
||||
? String::format( "%s (+%d / -%d)", mGitBranch.c_str(),
|
||||
mGitStatus.totalInserts, mGitStatus.totalDeletions )
|
||||
: mGitBranch );
|
||||
|
||||
std::string text;
|
||||
{
|
||||
Lock l( mGitStatusMutex );
|
||||
text = mStatusBarDisplayModifications &&
|
||||
( mGitStatus.totalInserts || mGitStatus.totalDeletions )
|
||||
? String::format( "%s (+%d / -%d)", gitBranch().c_str(), mGitStatus.totalInserts,
|
||||
mGitStatus.totalDeletions )
|
||||
: gitBranch();
|
||||
}
|
||||
mStatusButton->setText( text );
|
||||
|
||||
if ( !mStatusBarDisplayModifications )
|
||||
@@ -493,25 +497,39 @@ void GitPlugin::updateStatusBarSync() {
|
||||
}
|
||||
|
||||
void GitPlugin::updateStatus( bool force ) {
|
||||
if ( !mGit || !mGitFound || !mStatusBarDisplayBranch )
|
||||
if ( !mGit || !mGitFound || !mStatusBarDisplayBranch || mRunningUpdateStatus )
|
||||
return;
|
||||
mThreadPool->run( [this, force] {
|
||||
if ( !mGit )
|
||||
return;
|
||||
|
||||
if ( !mGit->getGitFolder().empty() ) {
|
||||
auto prevBranch = mGitBranch;
|
||||
mGitBranch = mGit->branch();
|
||||
auto prevGitStatus = mGitStatus;
|
||||
mGitStatus = mGit->status( mStatusRecurseSubmodules );
|
||||
if ( !force && mGitBranch == prevBranch && mGitStatus == prevGitStatus )
|
||||
mRunningUpdateStatus = true;
|
||||
mThreadPool->run(
|
||||
[this, force] {
|
||||
if ( !mGit )
|
||||
return;
|
||||
} else if ( !mStatusButton ) {
|
||||
return;
|
||||
}
|
||||
|
||||
getUISceneNode()->runOnMainThread( [this] { updateStatusBarSync(); } );
|
||||
} );
|
||||
if ( !mGit->getGitFolder().empty() ) {
|
||||
auto prevBranch = mGitBranch;
|
||||
{
|
||||
Lock l( mGitBranchMutex );
|
||||
mGitBranch = mGit->branch();
|
||||
}
|
||||
Git::Status prevGitStatus;
|
||||
{
|
||||
Lock l( mGitStatusMutex );
|
||||
prevGitStatus = mGitStatus;
|
||||
}
|
||||
Git::Status newGitStatus = mGit->status( mStatusRecurseSubmodules );
|
||||
{
|
||||
Lock l( mGitStatusMutex );
|
||||
mGitStatus = std::move( newGitStatus );
|
||||
if ( !force && mGitBranch == prevBranch && mGitStatus == prevGitStatus )
|
||||
return;
|
||||
}
|
||||
} else if ( !mStatusButton ) {
|
||||
return;
|
||||
}
|
||||
|
||||
getUISceneNode()->runOnMainThread( [this] { updateStatusBarSync(); } );
|
||||
},
|
||||
[this]( auto ) { mRunningUpdateStatus = false; } );
|
||||
}
|
||||
|
||||
PluginRequestHandle GitPlugin::processMessage( const PluginMessage& msg ) {
|
||||
@@ -520,11 +538,13 @@ PluginRequestHandle GitPlugin::processMessage( const PluginMessage& msg ) {
|
||||
if ( mGit ) {
|
||||
mGit->setProjectPath( msg.asJSON()["folder"] );
|
||||
updateUINow( true );
|
||||
mInitialized = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ecode::PluginMessageType::UIReady: {
|
||||
updateUINow();
|
||||
if ( !mInitialized )
|
||||
updateUINow();
|
||||
break;
|
||||
}
|
||||
case ecode::PluginMessageType::UIThemeReloaded: {
|
||||
@@ -638,7 +658,8 @@ bool GitPlugin::onMouseLeave( UICodeEditor* editor, const Vector2i&, const Uint3
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GitPlugin::gitBranch() const {
|
||||
std::string GitPlugin::gitBranch() {
|
||||
Lock l( mGitBranchMutex );
|
||||
return mGitBranch;
|
||||
}
|
||||
|
||||
@@ -751,25 +772,32 @@ bool GitPlugin::onKeyDown( UICodeEditor* editor, const KeyEvent& event ) {
|
||||
}
|
||||
|
||||
void GitPlugin::updateBranches() {
|
||||
if ( !mGit || !mGitFound )
|
||||
if ( !mGit || !mGitFound || mRunningUpdateBranches )
|
||||
return;
|
||||
|
||||
mThreadPool->run( [this] {
|
||||
if ( !mGit )
|
||||
return;
|
||||
mRunningUpdateBranches = true;
|
||||
mThreadPool->run(
|
||||
[this] {
|
||||
if ( !mGit || mGit->getGitFolder().empty() )
|
||||
return;
|
||||
|
||||
{
|
||||
Lock l( mGitBranchMutex );
|
||||
if ( mGitBranch.empty() )
|
||||
mGitBranch = mGit->branch();
|
||||
}
|
||||
|
||||
if ( !mGit->getGitFolder().empty() ) {
|
||||
if ( mGitBranch.empty() )
|
||||
mGitBranch = mGit->branch();
|
||||
auto branches = mGit->getAllBranchesAndTags();
|
||||
auto hash = hashBranches( branches );
|
||||
auto model = GitBranchModel::asModel( std::move( branches ), hash, this );
|
||||
|
||||
if ( mBranchesTree &&
|
||||
static_cast<GitBranchModel*>( mBranchesTree->getModel() )->getHash() == hash )
|
||||
return;
|
||||
|
||||
getUISceneNode()->runOnMainThread( [this, model] { updateBranchesUI( model ); } );
|
||||
}
|
||||
} );
|
||||
},
|
||||
[this]( auto ) { mRunningUpdateBranches = false; } );
|
||||
}
|
||||
|
||||
void GitPlugin::updateBranchesUI( std::shared_ptr<GitBranchModel> model ) {
|
||||
@@ -825,6 +853,7 @@ void GitPlugin::buildSidePanelTab() {
|
||||
|
||||
mBranchesTree->setAutoExpandOnSingleColumn( true );
|
||||
mBranchesTree->setHeadersVisible( false );
|
||||
mBranchesTree->setIndentWidth( 0 );
|
||||
mBranchesTree->on( Event::OnModelEvent, [this]( const Event* event ) {
|
||||
const ModelEvent* modelEvent = static_cast<const ModelEvent*>( event );
|
||||
if ( !modelEvent->getModelIndex().hasParent() )
|
||||
@@ -837,7 +866,10 @@ void GitPlugin::buildSidePanelTab() {
|
||||
case EE::UI::Abstract::ModelEventType::Open: {
|
||||
auto result = mGit->checkout( branch->name );
|
||||
if ( result.returnCode == EXIT_SUCCESS ) {
|
||||
mGitBranch = branch->name;
|
||||
{
|
||||
Lock l( mGitBranchMutex );
|
||||
mGitBranch = branch->name;
|
||||
}
|
||||
if ( mBranchesTree->getModel() )
|
||||
mBranchesTree->getModel()->invalidate( Model::DontInvalidateIndexes );
|
||||
} else {
|
||||
@@ -865,6 +897,7 @@ void GitPlugin::buildSidePanelTab() {
|
||||
|
||||
mStatusTree->setAutoColumnsWidth( true );
|
||||
mStatusTree->setHeadersVisible( false );
|
||||
mStatusTree->setIndentWidth( 0 );
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -16,7 +16,7 @@ class UITreeView;
|
||||
class UIDropDownList;
|
||||
class UIStackWidget;
|
||||
class UIListBoxItem;
|
||||
}
|
||||
} // namespace EE::UI
|
||||
|
||||
namespace ecode {
|
||||
|
||||
@@ -54,7 +54,7 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
bool onMouseLeave( UICodeEditor*, const Vector2i&, const Uint32& ) override;
|
||||
|
||||
std::string gitBranch() const;
|
||||
std::string gitBranch();
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Git> mGit;
|
||||
@@ -87,6 +87,7 @@ class GitPlugin : public PluginBase {
|
||||
bool mStatusRecurseSubmodules{ true };
|
||||
bool mOldDontAutoHideOnMouseMove{ false };
|
||||
bool mOldUsingCustomStyling{ false };
|
||||
bool mInitialized{ false };
|
||||
Uint32 mOldTextStyle{ 0 };
|
||||
Uint32 mOldTextAlign{ 0 };
|
||||
Color mOldBackgroundColor;
|
||||
@@ -100,6 +101,10 @@ class GitPlugin : public PluginBase {
|
||||
std::vector<UIWidget*> mStackMap;
|
||||
UIWidget* mGitContentView{ nullptr };
|
||||
UIWidget* mGitNoContentView{ nullptr };
|
||||
std::atomic<bool> mRunningUpdateBranches{ false };
|
||||
std::atomic<bool> mRunningUpdateStatus{ false };
|
||||
Mutex mGitBranchMutex;
|
||||
Mutex mGitStatusMutex;
|
||||
|
||||
struct CustomTokenizer {
|
||||
SyntaxDefinition def;
|
||||
|
||||
Reference in New Issue
Block a user