mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-10-01 10:40:20 +03:00
Improve Git History navigation and asynchronous UI safety
Add a History ref selector for HEAD, local branches, remote branches, and tags without requiring checkout. Reuse cached branch-model display strings to keep large ref lists efficient. Make deferred main-thread callbacks lifetime-safe across ecode controllers and plugins. Initialize plugin dispatchers during UIReady and extend MainThreadLifetime to support delayed and tagged dispatch. Fix expandable Git History rows, empty merge projections, and commit-description expansion. Display only the physical first line while collapsed and avoid duplicating it in the expanded body. Add coverage for history ref handling and preserve stale-request guards during repository and revision changes. Fix UIImageViewer dimension reporting for sprites by displaying the underlying texture dimensions. The Git History interface remains a work in progress.
This commit is contained in:
@@ -1263,6 +1263,10 @@ Für sichtbare Änderung ecode neu starten.</string>
|
||||
<string name="git_history_refresh">Aktualisieren</string>
|
||||
<string name="git_history_load_older">Ältere Commits laden</string>
|
||||
<string name="git_history_empty">Noch keine Commits</string>
|
||||
<string name="git_history_no_additional_commits">Keine weiteren Commits</string>
|
||||
<string name="git_history_error">Git-Verlauf konnte nicht geladen werden</string>
|
||||
<string name="git_history_retry">Wiederholen</string>
|
||||
<string name="git_local_branch">Lokal</string>
|
||||
<string name="git_remote_branch">Remote</string>
|
||||
<string name="git_tag">Tag</string>
|
||||
</resources>
|
||||
|
||||
@@ -1248,6 +1248,10 @@ Restart ecode to see the changes.</string>
|
||||
<string name="git_history_refresh">Refresh</string>
|
||||
<string name="git_history_load_older">Load older commits</string>
|
||||
<string name="git_history_empty">No commits yet</string>
|
||||
<string name="git_history_no_additional_commits">No additional commits</string>
|
||||
<string name="git_history_error">Could not load Git history</string>
|
||||
<string name="git_history_retry">Retry</string>
|
||||
<string name="git_local_branch">Local</string>
|
||||
<string name="git_remote_branch">Remote</string>
|
||||
<string name="git_tag">Tag</string>
|
||||
</resources>
|
||||
|
||||
@@ -1247,6 +1247,10 @@ Redémarrer ecode pour voir les changements.</string>
|
||||
<string name="git_history_refresh">Actualiser</string>
|
||||
<string name="git_history_load_older">Charger les commits précédents</string>
|
||||
<string name="git_history_empty">Aucun commit pour le moment</string>
|
||||
<string name="git_history_no_additional_commits">Aucun commit supplémentaire</string>
|
||||
<string name="git_history_error">Impossible de charger l’historique Git</string>
|
||||
<string name="git_history_retry">Réessayer</string>
|
||||
<string name="git_local_branch">Locale</string>
|
||||
<string name="git_remote_branch">Distante</string>
|
||||
<string name="git_tag">Tag</string>
|
||||
</resources>
|
||||
|
||||
@@ -1052,6 +1052,10 @@ file in the directory tree.</string>
|
||||
<string name="git_history_refresh">刷新</string>
|
||||
<string name="git_history_load_older">加载更早的提交</string>
|
||||
<string name="git_history_empty">尚无提交</string>
|
||||
<string name="git_history_no_additional_commits">没有其他提交</string>
|
||||
<string name="git_history_error">无法加载 Git 历史记录</string>
|
||||
<string name="git_history_retry">重试</string>
|
||||
<string name="git_local_branch">本地</string>
|
||||
<string name="git_remote_branch">远程</string>
|
||||
<string name="git_tag">标签</string>
|
||||
</resources>
|
||||
|
||||
@@ -134,6 +134,7 @@ class EE_API Event {
|
||||
OnApply,
|
||||
OnShowFindReplace,
|
||||
OnHideFindReplace,
|
||||
OnTooltipCreated,
|
||||
NoEvent = eeINDEX_NOT_FOUND
|
||||
};
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ template <typename T> class MainThreadLifetime {
|
||||
public:
|
||||
WeakHandle() = default;
|
||||
|
||||
void run( std::function<void( T* )> callback ) const {
|
||||
void run( std::function<void( T* )> callback, const Time& delay = Time::Zero,
|
||||
Action::UniqueID tag = 0 ) const {
|
||||
auto state = mState.lock();
|
||||
if ( !state )
|
||||
return;
|
||||
@@ -49,7 +50,8 @@ template <typename T> class MainThreadLifetime {
|
||||
}
|
||||
if ( object )
|
||||
callback( object );
|
||||
} );
|
||||
},
|
||||
delay, tag );
|
||||
}
|
||||
|
||||
explicit operator bool() const { return !mState.expired(); }
|
||||
|
||||
@@ -449,8 +449,20 @@ void UIImageViewer::updateTextDisplay() {
|
||||
if ( ( mDisplayOptions & DisplayOptions::DisplayDimensions ) && mImage->getDrawable() ) {
|
||||
str += i18n( "dimensions_colon", "Dimensions:" );
|
||||
str += " ";
|
||||
str += String::format( "%d x %d", (int)mImage->getDrawable()->getPixelsSize().x,
|
||||
(int)mImage->getDrawable()->getPixelsSize().y );
|
||||
if ( mImage->getDrawable()->getDrawableType() == Drawable::Type::SPRITE ) {
|
||||
auto ref = mImage->getDrawable();
|
||||
Sprite* spr = static_cast<Sprite*>( ref.get() );
|
||||
if ( spr->getCurrentTextureRegion() ) {
|
||||
auto tex = spr->getCurrentTextureRegion()->getTexture();
|
||||
if ( tex ) {
|
||||
str += String::format( "%d x %d", (int)tex->getImageWidth(),
|
||||
(int)tex->getImageHeight() );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
str += String::format( "%d x %d", (int)mImage->getDrawable()->getPixelsSize().x,
|
||||
(int)mImage->getDrawable()->getPixelsSize().y );
|
||||
}
|
||||
str += "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -415,6 +415,7 @@ UITooltip* UIWidget::createTooltip() {
|
||||
mTooltip->setTooltipOf( this );
|
||||
if ( !mTooltipText.empty() )
|
||||
mTooltip->setText( mTooltipText );
|
||||
sendCommonEvent( Event::OnTooltipCreated );
|
||||
return mTooltip;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,14 @@ UTEST( GitHistory, PaginatesFirstParentWithoutDuplicates ) {
|
||||
ASSERT_TRUE( third.success() );
|
||||
ASSERT_EQ( 1u, third.commits.size() );
|
||||
EXPECT_FALSE( third.hasMore );
|
||||
|
||||
ASSERT_EQ( EXIT_SUCCESS, run( { "branch", "older", "HEAD~2" } ) );
|
||||
Git::HistoryQuery refQuery;
|
||||
refQuery.revision = "refs/heads/older";
|
||||
auto selectedRef = git.history( refQuery, temp.path.string() );
|
||||
ASSERT_TRUE( selectedRef.success() );
|
||||
ASSERT_EQ( 3u, selectedRef.commits.size() );
|
||||
EXPECT_STREQ( "commit 2", selectedRef.commits.front().subject.c_str() );
|
||||
}
|
||||
|
||||
UTEST( GitHistory, PropagatedExclusionsHideAlreadyRepresentedMainline ) {
|
||||
@@ -205,12 +213,14 @@ UTEST( GitHistory, HandlesEmptyRepositoryUnicodeAndHardLimit ) {
|
||||
ASSERT_EQ( EXIT_SUCCESS, run( { "config", "user.name", "Tést 🚀" } ) );
|
||||
ASSERT_EQ( EXIT_SUCCESS, run( { "config", "user.email", "unicode@example.invalid" } ) );
|
||||
const std::string subject = "unicode 🚀 | quote \" and tab\tend";
|
||||
ASSERT_EQ( EXIT_SUCCESS, run( { "commit", "--allow-empty", "-m", subject } ) );
|
||||
const std::string message = subject + "\ncontinuation on a physical second line";
|
||||
ASSERT_EQ( EXIT_SUCCESS, run( { "commit", "--allow-empty", "-m", message } ) );
|
||||
query.limit = 200;
|
||||
auto page = git.history( query, temp.path.string() );
|
||||
ASSERT_TRUE( page.success() );
|
||||
ASSERT_EQ( 1u, page.commits.size() );
|
||||
EXPECT_STREQ( subject.c_str(), page.commits.front().subject.c_str() );
|
||||
EXPECT_STREQ( message.c_str(), page.commits.front().message.c_str() );
|
||||
EXPECT_STREQ( "Tést 🚀", page.commits.front().authorName.c_str() );
|
||||
}
|
||||
|
||||
|
||||
+22
-16
@@ -340,7 +340,8 @@ void App::setAppTitle( const std::string& title ) {
|
||||
if ( Engine::isMainThread() ) {
|
||||
mWindow->setTitle( fullTitle );
|
||||
} else {
|
||||
mUISceneNode->runOnMainThread( [this, fullTitle] { mWindow->setTitle( fullTitle ); } );
|
||||
mLifetime.weakHandle().run(
|
||||
[fullTitle]( App* app ) { app->mWindow->setTitle( fullTitle ); } );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -729,8 +730,9 @@ void App::initPluginManager() {
|
||||
} else {
|
||||
// If plugin loads asynchronously and is not ready, delay the plugin enabled callback
|
||||
plugin->addOnReadyCallback( [this]( UICodeEditorPlugin* plugin, const Uint32& cbId ) {
|
||||
mUISceneNode->runOnMainThread(
|
||||
[this, plugin]() { onPluginEnabled( static_cast<Plugin*>( plugin ) ); } );
|
||||
mLifetime.weakHandle().run( [plugin]( App* app ) {
|
||||
app->onPluginEnabled( static_cast<Plugin*>( plugin ) );
|
||||
} );
|
||||
plugin->removeReadyCallback( cbId );
|
||||
} );
|
||||
}
|
||||
@@ -1041,6 +1043,7 @@ void App::onTextDropped( String text ) {
|
||||
|
||||
App::App( const size_t& jobs, const std::vector<std::string>& args ) :
|
||||
mArgs( args ),
|
||||
mLifetime( this, nullptr ),
|
||||
mThreadPool(
|
||||
ThreadPool::createShared( jobs > 0 ? jobs : eemax<int>( 4, Sys::getCPUCount() ) ) ),
|
||||
mDateTimeController( std::make_unique<DateTimeController>( this ) ),
|
||||
@@ -1051,6 +1054,7 @@ App::App( const size_t& jobs, const std::vector<std::string>& args ) :
|
||||
}
|
||||
|
||||
App::~App() {
|
||||
mLifetime.invalidate();
|
||||
appInstance = nullptr;
|
||||
mDestroyingApp = true;
|
||||
|
||||
@@ -3588,10 +3592,10 @@ void App::loadDirTree( const std::string& path ) {
|
||||
Log::info( "DirTree read in: %s. Found %ld files.", clock.getElapsedTime().toString(),
|
||||
dirTree.getFilesCount() );
|
||||
mDirTreeReady = true;
|
||||
mUISceneNode->runOnMainThread( [this] {
|
||||
mUniversalLocator->updateFilesTable();
|
||||
if ( mSplitter->curEditorExistsAndFocused() )
|
||||
syncProjectTreeWithEditor( mSplitter->getCurEditor() );
|
||||
mLifetime.weakHandle().run( []( App* app ) {
|
||||
app->mUniversalLocator->updateFilesTable();
|
||||
if ( app->mSplitter->curEditorExistsAndFocused() )
|
||||
app->syncProjectTreeWithEditor( app->mSplitter->getCurEditor() );
|
||||
} );
|
||||
removeFolderWatches();
|
||||
if ( mFileWatcher ) {
|
||||
@@ -3772,11 +3776,11 @@ void App::newFile( const FileInfo& file ) {
|
||||
errorMsgBox( i18n( "couldnt_create_file", "Couldn't create file." ) );
|
||||
} else if ( mProjectTreeView ) {
|
||||
// We wait 100 ms to get the notification from the file system
|
||||
mUISceneNode->runOnMainThread(
|
||||
[this, newFilePath] {
|
||||
if ( !mFileSystemModel || !mProjectTreeView )
|
||||
mLifetime.weakHandle().run(
|
||||
[newFilePath]( App* app ) {
|
||||
if ( !app->mFileSystemModel || !app->mProjectTreeView )
|
||||
return;
|
||||
loadFileFromPathOrFocus( newFilePath );
|
||||
app->loadFileFromPathOrFocus( newFilePath );
|
||||
},
|
||||
Milliseconds( 100 ) );
|
||||
}
|
||||
@@ -3799,13 +3803,14 @@ void App::newFolder( const FileInfo& file ) {
|
||||
errorMsgBox( i18n( "couldnt_create_directory", "Couldn't create directory." ) );
|
||||
} else if ( mProjectTreeView ) {
|
||||
// We wait 100 ms to get the notification from the file system
|
||||
mUISceneNode->runOnMainThread(
|
||||
[this, newFolderPath] {
|
||||
if ( !mFileSystemModel || !mProjectTreeView )
|
||||
mLifetime.weakHandle().run(
|
||||
[newFolderPath]( App* app ) {
|
||||
if ( !app->mFileSystemModel || !app->mProjectTreeView )
|
||||
return;
|
||||
std::string nfp( newFolderPath );
|
||||
FileSystem::filePathRemoveBasePath( mFileSystemModel->getRootPath(), nfp );
|
||||
mProjectTreeView->openRowWithPath( nfp );
|
||||
FileSystem::filePathRemoveBasePath( app->mFileSystemModel->getRootPath(),
|
||||
nfp );
|
||||
app->mProjectTreeView->openRowWithPath( nfp );
|
||||
},
|
||||
Milliseconds( 100 ) );
|
||||
}
|
||||
@@ -4929,6 +4934,7 @@ void App::init( InitParameters& params ) {
|
||||
eemax( mWindow->getScale(), mConfig.windowState.pixelDensity ) );
|
||||
|
||||
mUISceneNode = UISceneNode::New();
|
||||
mLifetime.setDispatcher( mUISceneNode );
|
||||
mUISceneNode->setThreadPool( mThreadPool );
|
||||
mUIColorScheme = mConfig.ui.colorScheme;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "uistatusbar.hpp"
|
||||
#include "universallocator.hpp"
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <eepp/ui/uinodelink.hpp>
|
||||
#include <efsw/efsw.hpp>
|
||||
#include <eterm/ui/uiterminal.hpp>
|
||||
@@ -672,6 +673,7 @@ class App : public UICodeEditorSplitter::Client, public PluginContextProvider {
|
||||
std::vector<std::string> mArgs;
|
||||
EE::Window::Window* mWindow{ nullptr };
|
||||
UISceneNode* mUISceneNode{ nullptr };
|
||||
MainThreadLifetime<App> mLifetime;
|
||||
UIConsole* mConsole{ nullptr };
|
||||
std::string mCurWindowTitle;
|
||||
std::string mWindowTitle{ "ecode" };
|
||||
|
||||
@@ -7,7 +7,10 @@ static int LOCATEBAR_MAX_VISIBLE_ITEMS = 18;
|
||||
|
||||
GlobalSearchController::GlobalSearchController( UICodeEditorSplitter* editorSplitter,
|
||||
UISceneNode* sceneNode, App* app ) :
|
||||
mSplitter( editorSplitter ), mUISceneNode( sceneNode ), mApp( app ) {
|
||||
mSplitter( editorSplitter ),
|
||||
mUISceneNode( sceneNode ),
|
||||
mLifetime( this, sceneNode ),
|
||||
mApp( app ) {
|
||||
mApp->getPluginManager()->subscribeMessages(
|
||||
"GlobalSearchController", [this]( const PluginMessage& msg ) -> PluginRequestHandle {
|
||||
return processMessage( msg );
|
||||
@@ -831,20 +834,22 @@ void GlobalSearchController::doGlobalSearch( String text, String filter, bool ca
|
||||
filter]( const ProjectSearch::ConsolidatedResult& res ) {
|
||||
Log::info( "Global search for \"%s\" took %s", search.c_str(),
|
||||
clock.getElapsedTime().toString() );
|
||||
mUISceneNode->runOnMainThread( [this, res = std::move( res ), search, searchReplace,
|
||||
searchAgain, escapeSequence, searchType, filter] {
|
||||
mLastSearchConfig = std::move( res.first );
|
||||
mLifetime.weakHandle().run( [res = std::move( res ), search, searchReplace, searchAgain,
|
||||
escapeSequence, searchType,
|
||||
filter]( GlobalSearchController* controller ) mutable {
|
||||
controller->mLastSearchConfig = std::move( res.first );
|
||||
auto model = ProjectSearch::asModel( res.second );
|
||||
model->setOpType( searchType );
|
||||
updateGlobalSearchHistory( model, search, filter, searchReplace, searchAgain,
|
||||
escapeSequence );
|
||||
updateGlobalSearchBarResults( search, model, searchReplace, escapeSequence );
|
||||
auto* loader = mGlobalSearchLayout->getParent()->find( "loader" );
|
||||
controller->updateGlobalSearchHistory( model, search, filter, searchReplace,
|
||||
searchAgain, escapeSequence );
|
||||
controller->updateGlobalSearchBarResults( search, model, searchReplace,
|
||||
escapeSequence );
|
||||
auto* loader = controller->mGlobalSearchLayout->getParent()->find( "loader" );
|
||||
if ( loader ) {
|
||||
loader->setVisible( false );
|
||||
loader->close();
|
||||
}
|
||||
mCurSearch = nullptr;
|
||||
controller->mCurSearch = nullptr;
|
||||
} );
|
||||
},
|
||||
caseSensitive, wholeWord, searchType, filters, mApp->getCurrentProject(), openDocs );
|
||||
@@ -951,12 +956,12 @@ PluginRequestHandle GlobalSearchController::processMessage( const PluginMessage&
|
||||
sample.line.substr( sample.position.start().column(),
|
||||
sample.position.end().column() - sample.position.start().column() );
|
||||
|
||||
mUISceneNode->runOnMainThread( [this, search, model] {
|
||||
showGlobalSearch( false );
|
||||
mGlobalSearchInput->setText( search );
|
||||
mGlobalSearchWhereInput->setText( "" );
|
||||
updateGlobalSearchHistory( model, search, "", false, false, false );
|
||||
updateGlobalSearchBarResults( search, model, false, false );
|
||||
mLifetime.weakHandle().run( [search, model]( GlobalSearchController* controller ) {
|
||||
controller->showGlobalSearch( false );
|
||||
controller->mGlobalSearchInput->setText( search );
|
||||
controller->mGlobalSearchWhereInput->setText( "" );
|
||||
controller->updateGlobalSearchHistory( model, search, "", false, false, false );
|
||||
controller->updateGlobalSearchBarResults( search, model, false, false );
|
||||
} );
|
||||
|
||||
return {};
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "plugins/pluginmanager.hpp"
|
||||
#include "projectsearch.hpp"
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
|
||||
namespace ecode {
|
||||
|
||||
@@ -68,6 +69,7 @@ class GlobalSearchController {
|
||||
protected:
|
||||
UICodeEditorSplitter* mSplitter{ nullptr };
|
||||
UISceneNode* mUISceneNode{ nullptr };
|
||||
MainThreadLifetime<GlobalSearchController> mLifetime;
|
||||
App* mApp{ nullptr };
|
||||
|
||||
UIGlobalSearchBar* mGlobalSearchBarLayout{ nullptr };
|
||||
|
||||
@@ -608,6 +608,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
UILinearLayout(),
|
||||
WidgetCommandExecuter( getInput() ),
|
||||
mManager( manager ),
|
||||
mLifetime( this, getUISceneNode() ),
|
||||
mDisplayReasoning( getPlugin() && getPlugin()->displayReasoning() ) {
|
||||
setClass( "llm_chatui" );
|
||||
setLayoutSizePolicy( SizePolicy::MatchParent, SizePolicy::MatchParent );
|
||||
@@ -1171,6 +1172,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) :
|
||||
}
|
||||
|
||||
LLMChatUI::~LLMChatUI() {
|
||||
mLifetime.invalidate();
|
||||
if ( mRequest ) {
|
||||
mRequest->cancelCb = nullptr;
|
||||
mRequest->doneCb = nullptr;
|
||||
@@ -3343,11 +3345,12 @@ void LLMChatUI::showAttachFile() {
|
||||
ctx->getDirTree()->asyncMatchTree(
|
||||
ProjectDirectoryTree::MatchType::Fuzzy, text, 100,
|
||||
[this, text]( auto res ) {
|
||||
mUISceneNode->runOnMainThread( [this, res] {
|
||||
mLocateTable->setModel( res );
|
||||
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
|
||||
mLocateTable->scrollToTop();
|
||||
updateLocateBarColumns();
|
||||
mLifetime.weakHandle().run( [res]( LLMChatUI* chat ) {
|
||||
chat->mLocateTable->setModel( res );
|
||||
chat->mLocateTable->getSelection().set(
|
||||
chat->mLocateTable->getModel()->index( 0 ) );
|
||||
chat->mLocateTable->scrollToTop();
|
||||
chat->updateLocateBarColumns();
|
||||
} );
|
||||
},
|
||||
ctx->getCurrentProject() );
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "llmchatcompletionrequest.hpp"
|
||||
#include "protocol.hpp"
|
||||
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <eepp/ui/uilinearlayout.hpp>
|
||||
#include <eepp/ui/widgetcommandexecuter.hpp>
|
||||
|
||||
@@ -102,6 +103,7 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter {
|
||||
std::string mSummary;
|
||||
long mTimestamp{ 0 };
|
||||
PluginManager* mManager{ nullptr };
|
||||
MainThreadLifetime<LLMChatUI> mLifetime;
|
||||
UISplitter* mChatSplitter{ nullptr };
|
||||
UIWidget* mChatsList{ nullptr };
|
||||
UICodeEditor* mChatInput{ nullptr };
|
||||
|
||||
@@ -189,7 +189,7 @@ void DebuggerClientListener::initUI() {
|
||||
|
||||
void DebuggerClientListener::stateChanged( DebuggerClient::State state, const SessionId& ) {
|
||||
if ( state == DebuggerClient::State::Initializing ) {
|
||||
mPlugin->getManager()->getUISceneNode()->runOnMainThread( [this] { initUI(); } );
|
||||
mPlugin->mLifetime.weakHandle().run( [this]( DebuggerPlugin* ) { initUI(); } );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,9 +345,9 @@ void DebuggerClientListener::outputProduced( const Output& output ) {
|
||||
auto buffer = output.output;
|
||||
auto sdc = getStatusDebuggerController();
|
||||
if ( sdc == nullptr || sdc->getUIConsole() == nullptr ) {
|
||||
mPlugin->getUISceneNode()->runOnMainThread(
|
||||
[this, buffer = std::move( buffer )]() mutable {
|
||||
mPlugin->initStatusDebuggerController();
|
||||
mPlugin->mLifetime.weakHandle().run(
|
||||
[this, buffer = std::move( buffer )]( DebuggerPlugin* plugin ) mutable {
|
||||
plugin->initStatusDebuggerController();
|
||||
auto sdc = getStatusDebuggerController();
|
||||
sdc->insertConsoleBuffer( std::move( buffer ) );
|
||||
} );
|
||||
@@ -407,8 +407,9 @@ void DebuggerClientListener::changeScope( const StackFrame& f ) {
|
||||
TextRange range{ { f.line - 1, f.column - 1 }, { f.line - 1, f.column - 1 } };
|
||||
std::string path( f.source->path );
|
||||
|
||||
mPlugin->getUISceneNode()->runOnMainThread(
|
||||
[this, path, range] { mPlugin->getPluginContext()->focusOrLoadFile( path, range ); } );
|
||||
mPlugin->mLifetime.weakHandle().run( [path, range]( DebuggerPlugin* plugin ) {
|
||||
plugin->getPluginContext()->focusOrLoadFile( path, range );
|
||||
} );
|
||||
|
||||
auto sdc = getStatusDebuggerController();
|
||||
if ( sdc && sdc->getUIStack() )
|
||||
|
||||
@@ -155,7 +155,7 @@ Plugin* DebuggerPlugin::NewSync( PluginManager* pluginManager ) {
|
||||
}
|
||||
|
||||
DebuggerPlugin::DebuggerPlugin( PluginManager* pluginManager, bool sync ) :
|
||||
PluginBase( pluginManager ) {
|
||||
PluginBase( pluginManager ), mLifetime( this, getUISceneNode() ) {
|
||||
if ( sync ) {
|
||||
load( pluginManager );
|
||||
} else {
|
||||
@@ -164,6 +164,7 @@ DebuggerPlugin::DebuggerPlugin( PluginManager* pluginManager, bool sync ) :
|
||||
}
|
||||
|
||||
DebuggerPlugin::~DebuggerPlugin() {
|
||||
mLifetime.invalidate();
|
||||
waitUntilLoaded();
|
||||
mShuttingDown = true;
|
||||
|
||||
@@ -712,6 +713,7 @@ void DebuggerPlugin::loadProjectConfiguration( const std::string& path ) {
|
||||
}
|
||||
|
||||
void DebuggerPlugin::loadProjectConfigurations() {
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
if ( mProjectPath.empty() )
|
||||
return;
|
||||
|
||||
@@ -720,7 +722,7 @@ void DebuggerPlugin::loadProjectConfigurations() {
|
||||
mDapConfigs.clear();
|
||||
}
|
||||
|
||||
mThreadPool->run( [this] {
|
||||
mThreadPool->run( [this, lifetime] {
|
||||
std::string config;
|
||||
if ( mLoadVSCodeLaunchConfig ) {
|
||||
config = mProjectPath + ".vscode/launch.json";
|
||||
@@ -731,9 +733,9 @@ void DebuggerPlugin::loadProjectConfigurations() {
|
||||
if ( FileSystem::fileExists( config ) )
|
||||
loadProjectConfiguration( config );
|
||||
|
||||
getUISceneNode()->runOnMainThread( [this] {
|
||||
updateDebuggerConfigurationList();
|
||||
updateSelectedDebugConfig();
|
||||
lifetime.run( []( DebuggerPlugin* plugin ) {
|
||||
plugin->updateDebuggerConfigurationList();
|
||||
plugin->updateSelectedDebugConfig();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
@@ -744,11 +746,11 @@ PluginRequestHandle DebuggerPlugin::processMessage( const PluginMessage& msg ) {
|
||||
mProjectPath = msg.asJSON()["folder"];
|
||||
|
||||
if ( getUISceneNode() && mSidePanel ) {
|
||||
getUISceneNode()->runOnMainThread( [this] {
|
||||
if ( mProjectPath.empty() ) {
|
||||
hideSidePanel();
|
||||
Lock l( mDapsMutex );
|
||||
mDapConfigs.clear();
|
||||
mLifetime.weakHandle().run( []( DebuggerPlugin* plugin ) {
|
||||
if ( plugin->mProjectPath.empty() ) {
|
||||
plugin->hideSidePanel();
|
||||
Lock l( plugin->mDapsMutex );
|
||||
plugin->mDapConfigs.clear();
|
||||
}
|
||||
} );
|
||||
}
|
||||
@@ -760,6 +762,7 @@ PluginRequestHandle DebuggerPlugin::processMessage( const PluginMessage& msg ) {
|
||||
break;
|
||||
}
|
||||
case ecode::PluginMessageType::UIReady: {
|
||||
mLifetime.setDispatcher( getUISceneNode() );
|
||||
registerCommands( getPluginContext()->getMainLayout() );
|
||||
for ( const auto& kb : mKeyBindings ) {
|
||||
getPluginContext()->getMainLayout()->getKeyBindings().addKeybindString( kb.second,
|
||||
@@ -784,9 +787,9 @@ void DebuggerPlugin::updateUI() {
|
||||
if ( !getUISceneNode() )
|
||||
return;
|
||||
|
||||
getUISceneNode()->runOnMainThread( [this] {
|
||||
buildSidePanelTab();
|
||||
buildStatusBar();
|
||||
mLifetime.weakHandle().run( []( DebuggerPlugin* plugin ) {
|
||||
plugin->buildSidePanelTab();
|
||||
plugin->buildStatusBar();
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -1495,15 +1498,16 @@ void DebuggerPlugin::registerCommands( TCommandRegister* executer ) {
|
||||
if ( exitCode == 0 ) {
|
||||
runCurrentConfig();
|
||||
} else {
|
||||
getPluginContext()->getUISceneNode()->runOnMainThread( [this] {
|
||||
mLifetime.weakHandle().run( []( DebuggerPlugin* plugin ) {
|
||||
auto msgBox = UIMessageBox::New(
|
||||
UIMessageBox::YES_NO,
|
||||
i18n( "build_failed_debug_anyways",
|
||||
"Building the project failed, do you want to "
|
||||
"debug the binary anyways?" ) );
|
||||
msgBox->setTitle( i18n( "build_failed", "Build Failed" ) );
|
||||
plugin->i18n( "build_failed_debug_anyways",
|
||||
"Building the project failed, do you want to "
|
||||
"debug the binary anyways?" ) );
|
||||
msgBox->setTitle( plugin->i18n( "build_failed", "Build Failed" ) );
|
||||
msgBox->setCloseShortcut( { KEY_ESCAPE, KEYMOD_NONE } );
|
||||
msgBox->on( Event::OnConfirm, [this]( auto ) { runCurrentConfig(); } );
|
||||
msgBox->on( Event::OnConfirm,
|
||||
[plugin]( auto ) { plugin->runCurrentConfig(); } );
|
||||
msgBox->showWhenReady();
|
||||
} );
|
||||
}
|
||||
@@ -2348,11 +2352,12 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc
|
||||
std::function<void( int )> doneFn ) {
|
||||
if ( !FileSystem::fileExists( cmd ) )
|
||||
cmd = FileSystem::fileNameFromPath( cmd );
|
||||
getUISceneNode()->runOnMainThread( [this, isIntegrated, cmd = std::move( cmd ), cwd, args,
|
||||
doneFn = std::move( doneFn ), env = std::move( env )] {
|
||||
mLifetime.weakHandle().run( [isIntegrated, cmd = std::move( cmd ), cwd, args,
|
||||
doneFn = std::move( doneFn ),
|
||||
env = std::move( env )]( DebuggerPlugin* plugin ) {
|
||||
if ( isIntegrated || !env.empty() ) {
|
||||
UITerminal* term =
|
||||
getPluginContext()->getTerminalManager()->createTerminalInSplitter(
|
||||
plugin->getPluginContext()->getTerminalManager()->createTerminalInSplitter(
|
||||
cwd, cmd, args, env, false, false );
|
||||
|
||||
doneFn( term && term->getTerm() && term->getTerm()->getTerminal() &&
|
||||
@@ -2361,15 +2366,16 @@ void DebuggerPlugin::run( const std::string& debugger, ProtocolSettings&& protoc
|
||||
: 0 );
|
||||
} else {
|
||||
std::string fcmd = cmd + ( !args.empty() ? " " : "" ) + String::join( args, ' ' );
|
||||
doneFn(
|
||||
getPluginContext()->getTerminalManager()->openInExternalTerminal( fcmd, cwd ) );
|
||||
doneFn( plugin->getPluginContext()->getTerminalManager()->openInExternalTerminal(
|
||||
fcmd, cwd ) );
|
||||
}
|
||||
} );
|
||||
};
|
||||
|
||||
dap->runTargetCb = [this] {
|
||||
getUISceneNode()->runOnMainThread(
|
||||
[this] { getPluginContext()->runCommand( "project-run-executable" ); } );
|
||||
mLifetime.weakHandle().run( []( DebuggerPlugin* plugin ) {
|
||||
plugin->getPluginContext()->runCommand( "project-run-executable" );
|
||||
} );
|
||||
};
|
||||
|
||||
mDebugger->start();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "config.hpp"
|
||||
#include "debuggerclientlistener.hpp"
|
||||
#include "models/breakpointsmodel.hpp"
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
|
||||
using namespace EE::UI::Models;
|
||||
using namespace EE::UI;
|
||||
@@ -90,6 +91,7 @@ class DebuggerPlugin : public PluginBase {
|
||||
|
||||
protected:
|
||||
friend class DebuggerClientListener;
|
||||
MainThreadLifetime<DebuggerPlugin> mLifetime;
|
||||
|
||||
bool mInitialized{ false };
|
||||
bool mFetchRegisters{ false };
|
||||
|
||||
@@ -141,11 +141,12 @@ StatusDebuggerController::getLocalDefaultKeybindings() {
|
||||
StatusDebuggerController::StatusDebuggerController( UISplitter* mainSplitter,
|
||||
UISceneNode* uiSceneNode,
|
||||
PluginContextProvider* pluginContext ) :
|
||||
StatusBarElement( mainSplitter, uiSceneNode, pluginContext ) {
|
||||
StatusBarElement( mainSplitter, uiSceneNode, pluginContext ), mLifetime( this, uiSceneNode ) {
|
||||
mSerializedLayout = mContext->getConfig().iniState.getValue( "debugger", "panel_layout" );
|
||||
}
|
||||
|
||||
StatusDebuggerController::~StatusDebuggerController() {
|
||||
mLifetime.invalidate();
|
||||
mEventConnections.clear();
|
||||
mTabWidgetEventConnections.clear();
|
||||
if ( mContainer )
|
||||
@@ -232,20 +233,20 @@ void StatusDebuggerController::setDebuggingState( State state ) {
|
||||
if ( !mContainer )
|
||||
return;
|
||||
|
||||
mUISceneNode->runOnMainThread( [this, state] {
|
||||
mUIButStart->setVisible( state == State::NotStarted )
|
||||
mLifetime.weakHandle().run( [state]( StatusDebuggerController* controller ) {
|
||||
controller->mUIButStart->setVisible( state == State::NotStarted )
|
||||
->setEnabled( state == State::NotStarted );
|
||||
mUIButStop->setVisible( state != State::NotStarted )
|
||||
controller->mUIButStop->setVisible( state != State::NotStarted )
|
||||
->setEnabled( state != State::NotStarted );
|
||||
mUIButContinue->setVisible( state != State::NotStarted )
|
||||
controller->mUIButContinue->setVisible( state != State::NotStarted )
|
||||
->setEnabled( state == State::Paused );
|
||||
mUIButPause->setVisible( state != State::NotStarted )
|
||||
controller->mUIButPause->setVisible( state != State::NotStarted )
|
||||
->setEnabled( state == State::Running );
|
||||
mUIButStepOver->setVisible( state != State::NotStarted )
|
||||
controller->mUIButStepOver->setVisible( state != State::NotStarted )
|
||||
->setEnabled( state == State::Paused );
|
||||
mUIButStepInto->setVisible( state != State::NotStarted )
|
||||
controller->mUIButStepInto->setVisible( state != State::NotStarted )
|
||||
->setEnabled( state == State::Paused );
|
||||
mUIButStepOut->setVisible( state != State::NotStarted )
|
||||
controller->mUIButStepOut->setVisible( state != State::NotStarted )
|
||||
->setEnabled( state == State::Paused );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "models/breakpointsmodel.hpp"
|
||||
#include <eepp/core/containers.hpp>
|
||||
#include <eepp/scene/eventconnection.hpp>
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
|
||||
#include <eepp/ui/tools/uitabwidgetsplitter.hpp>
|
||||
@@ -113,6 +114,7 @@ class StatusDebuggerController : public StatusBarElement, public UITabWidgetSpli
|
||||
bool mRestoringLayout{ false };
|
||||
bool mRightPanelDropPreview{ false };
|
||||
std::string mSerializedLayout;
|
||||
MainThreadLifetime<StatusDebuggerController> mLifetime;
|
||||
|
||||
void createContainer();
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Git::HistoryPage Git::history( const HistoryQuery& query, const std::string& pro
|
||||
"--first-parent",
|
||||
String::format( "--max-count=%zu", query.limit + 1 ),
|
||||
"-z",
|
||||
"--format=%H%x00%h%x00%P%x00%an%x00%ae%x00%at%x00%ct%x00%s",
|
||||
"--format=%H%x00%h%x00%P%x00%an%x00%ae%x00%at%x00%ct%x00%B",
|
||||
query.continuation.empty() ? query.revision
|
||||
: query.continuation };
|
||||
if ( !query.exclusions.empty() ) {
|
||||
@@ -86,7 +86,12 @@ Git::HistoryPage Git::history( const HistoryQuery& query, const std::string& pro
|
||||
}
|
||||
commit.authorName = fields[3];
|
||||
commit.authorEmail = fields[4];
|
||||
commit.subject = fields[7];
|
||||
commit.message = fields[7];
|
||||
while ( !commit.message.empty() &&
|
||||
( commit.message.back() == '\n' || commit.message.back() == '\r' ) )
|
||||
commit.message.pop_back();
|
||||
const size_t subjectEnd = commit.message.find_first_of( "\r\n" );
|
||||
commit.subject = commit.message.substr( 0, subjectEnd );
|
||||
if ( !parseHistoryTimestamp( fields[5], commit.authorTime ) ||
|
||||
!parseHistoryTimestamp( fields[6], commit.commitTime ) ) {
|
||||
page.returnCode = EXIT_FAILURE;
|
||||
|
||||
@@ -206,6 +206,7 @@ class Git {
|
||||
std::string shortHash;
|
||||
std::vector<std::string> parents;
|
||||
std::string subject;
|
||||
std::string message;
|
||||
std::string authorName;
|
||||
std::string authorEmail;
|
||||
int64_t authorTime{ 0 };
|
||||
|
||||
@@ -37,8 +37,27 @@ GitBranchModel::GitBranchModel( std::vector<Git::Branch>&& branches, size_t hash
|
||||
type.emplace_back( std::move( branch ) );
|
||||
}
|
||||
for ( auto& branch : branchTypes ) {
|
||||
mBranches.emplace_back(
|
||||
BranchData{ std::move( branch.first ), std::move( branch.second ) } );
|
||||
BranchData data{ std::move( branch.first ), std::move( branch.second ), {} };
|
||||
data.historyDisplay.reserve( data.data.size() );
|
||||
for ( const auto& ref : data.data ) {
|
||||
String kind;
|
||||
switch ( ref.type ) {
|
||||
case Git::RefType::Head:
|
||||
kind = mPlugin->i18n( "git_local_branch", "Local" );
|
||||
break;
|
||||
case Git::RefType::Remote:
|
||||
kind = mPlugin->i18n( "git_remote_branch", "Remote" );
|
||||
break;
|
||||
case Git::RefType::Tag:
|
||||
kind = mPlugin->i18n( "git_tag", "Tag" );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
data.historyDisplay.emplace_back( kind + String{ " · " } +
|
||||
String::fromUtf8( ref.name ) );
|
||||
}
|
||||
mBranches.emplace_back( std::move( data ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +138,8 @@ Variant GitBranchModel::data( const ModelIndex& index, ModelRole role ) const {
|
||||
return Variant( branch.typeStr() );
|
||||
case Column::LastCommit:
|
||||
return Variant( branch.lastCommit.c_str() );
|
||||
case Column::HistoryDisplay:
|
||||
return Variant( &mBranches[index.internalId()].historyDisplay[index.row()] );
|
||||
}
|
||||
return Variant( GIT_EMPTY );
|
||||
}
|
||||
@@ -166,6 +187,10 @@ Variant GitBranchModel::data( const ModelIndex& index, ModelRole role ) const {
|
||||
}
|
||||
|
||||
Git::Branch GitBranchModel::branch( const ModelIndex& index ) const {
|
||||
return branchRef( index );
|
||||
}
|
||||
|
||||
const Git::Branch& GitBranchModel::branchRef( const ModelIndex& index ) const {
|
||||
return *static_cast<Git::Branch*>( index.internalData() );
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,12 @@ class GitBranchModel : public Model {
|
||||
|
||||
static size_t hashBranches( const std::vector<Git::Branch>& branches );
|
||||
|
||||
enum Column { Name, Remote, Type, LastCommit };
|
||||
enum Column { Name, Remote, Type, LastCommit, HistoryDisplay };
|
||||
|
||||
struct BranchData {
|
||||
std::string branch;
|
||||
std::vector<Git::Branch> data;
|
||||
std::vector<String> historyDisplay;
|
||||
};
|
||||
|
||||
std::string refTypeToString( Git::RefType type ) const;
|
||||
@@ -40,7 +41,7 @@ class GitBranchModel : public Model {
|
||||
|
||||
size_t rowCount( const ModelIndex& index ) const;
|
||||
|
||||
size_t columnCount( const ModelIndex& ) const { return 4; }
|
||||
size_t columnCount( const ModelIndex& ) const { return 5; }
|
||||
|
||||
ModelIndex parentIndex( const ModelIndex& index ) const;
|
||||
|
||||
@@ -58,6 +59,8 @@ class GitBranchModel : public Model {
|
||||
|
||||
Git::Branch branch( const ModelIndex& index ) const;
|
||||
|
||||
const Git::Branch& branchRef( const ModelIndex& index ) const;
|
||||
|
||||
std::optional<Git::Branch> branch( const std::string& name ) const;
|
||||
|
||||
ModelIndex refTypeIndex( Git::RefType refType ) const;
|
||||
|
||||
@@ -111,7 +111,7 @@ GitHistoryModel::commitNode( Git::Commit commit, Node* parent,
|
||||
item->date = Sys::epochToString( item->commit.commitTime );
|
||||
item->hash = String::fromUtf8( item->commit.shortHash );
|
||||
item->tooltip = String::format( "%s\n%s <%s>\n%s", item->commit.hash, item->commit.authorName,
|
||||
item->commit.authorEmail, item->commit.subject );
|
||||
item->commit.authorEmail, item->commit.message );
|
||||
item->message = item->date + String{ " · " } + item->author + String{ " · " } + item->hash;
|
||||
if ( item->commit.parents.size() == 2 ) {
|
||||
item->query = query;
|
||||
@@ -207,6 +207,14 @@ void GitHistoryModel::setChildrenPage( Node* item, Git::HistoryPage page,
|
||||
return;
|
||||
Nodes children;
|
||||
fillPage( children, item, std::move( page ), query );
|
||||
if ( children.empty() ) {
|
||||
auto child = std::make_unique<Node>();
|
||||
child->type = NodeType::Empty;
|
||||
child->parent = item;
|
||||
child->message =
|
||||
mPlugin->i18n( "git_history_no_additional_commits", "No additional commits" );
|
||||
children.emplace_back( std::move( child ) );
|
||||
}
|
||||
item->childrenLoading = false;
|
||||
item->childrenLoaded = true;
|
||||
replaceChildren( item, std::move( children ) );
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "githistorytreeview.hpp"
|
||||
#include "githistorymodel.hpp"
|
||||
#include <eepp/ui/uistyle.hpp>
|
||||
#include <eepp/ui/uitooltip.hpp>
|
||||
|
||||
namespace ecode {
|
||||
|
||||
@@ -9,6 +10,12 @@ GitHistoryTreeViewCell::GitHistoryTreeViewCell() :
|
||||
mHintColor( Color::fromString(
|
||||
mUISceneNode->getRoot()->getUIStyle()->getVariable( "--font-hint" ).getValue() ) ) {
|
||||
mTextBox->setTextOverflow( "ellipsis" );
|
||||
on( Event::OnTooltipCreated, []( const Event* event ) {
|
||||
auto* tooltip = event->getNode()->asType<UIWidget>()->getTooltip();
|
||||
tooltip->setMaxWidthEq( "60%" );
|
||||
tooltip->setWordWrap( true );
|
||||
tooltip->setHorizontalAlign( UI_HALIGN_LEFT );
|
||||
} );
|
||||
}
|
||||
|
||||
Sizef GitHistoryTreeViewCell::updateLayout() {
|
||||
@@ -58,4 +65,13 @@ UIWidget* GitHistoryTreeView::createCell( UIWidget* rowWidget, const ModelIndex&
|
||||
return setupCell( widget, rowWidget, index );
|
||||
}
|
||||
|
||||
UIWidget* GitHistoryTreeView::updateCell( const Vector2<Int64>& posIndex, const ModelIndex& index,
|
||||
const size_t& indentLevel, const Float& yOffset ) {
|
||||
UIWidget* widget = UITreeView::updateCell( posIndex, index, indentLevel, yOffset );
|
||||
if ( index.isValid() && index.column() == static_cast<Int64>( getModel()->treeColumn() ) &&
|
||||
getExpandersAsIcons() && getModel()->hasChildren( index ) )
|
||||
static_cast<GitHistoryTreeViewCell*>( widget )->updateCell( getModel() );
|
||||
return widget;
|
||||
}
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -33,6 +33,9 @@ class GitHistoryTreeView : public UITreeView {
|
||||
|
||||
protected:
|
||||
GitHistoryTreeView() = default;
|
||||
|
||||
UIWidget* updateCell( const Vector2<Int64>& posIndex, const ModelIndex& index,
|
||||
const size_t& indentLevel, const Float& yOffset ) override;
|
||||
};
|
||||
|
||||
} // namespace ecode
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <eepp/ui/tools/uimergeview.hpp>
|
||||
#include <eepp/ui/uicheckbox.hpp>
|
||||
#include <eepp/ui/uidropdownlist.hpp>
|
||||
#include <eepp/ui/uidropdownmodellist.hpp>
|
||||
#include <eepp/ui/uiiconthememanager.hpp>
|
||||
#include <eepp/ui/uiloader.hpp>
|
||||
#include <eepp/ui/uipopupmenu.hpp>
|
||||
@@ -39,6 +40,73 @@ using json = nlohmann::json;
|
||||
|
||||
namespace ecode {
|
||||
|
||||
GitHistoryRefModel::GitHistoryRefModel( std::shared_ptr<GitBranchModel> source, String headLabel ) :
|
||||
mSource( std::move( source ) ), mHeadLabel( std::move( headLabel ) ) {
|
||||
if ( !mSource )
|
||||
return;
|
||||
for ( size_t groupRow = 0; groupRow < mSource->rowCount( {} ); ++groupRow ) {
|
||||
const ModelIndex group = mSource->index( groupRow, GitBranchModel::Name, {} );
|
||||
for ( size_t row = 0; row < mSource->rowCount( group ); ++row ) {
|
||||
const ModelIndex index = mSource->index( row, GitBranchModel::HistoryDisplay, group );
|
||||
if ( mSource->branchRef( index ).type != Git::RefType::Stash )
|
||||
mSourceIndexes.emplace_back( index );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModelIndex GitHistoryRefModel::index( int row, int column, const ModelIndex& parent ) const {
|
||||
if ( parent.isValid() || row < 0 || static_cast<size_t>( row ) >= rowCount() || column != 0 )
|
||||
return {};
|
||||
return createIndex( row, column );
|
||||
}
|
||||
|
||||
Variant GitHistoryRefModel::data( const ModelIndex& index, ModelRole role ) const {
|
||||
if ( role != ModelRole::Display || !index.isValid() || index.row() < 0 ||
|
||||
static_cast<size_t>( index.row() ) >= rowCount() )
|
||||
return {};
|
||||
if ( index.row() == 0 )
|
||||
return Variant( &mHeadLabel );
|
||||
return mSource->data( mSourceIndexes[index.row() - 1], role );
|
||||
}
|
||||
|
||||
std::string GitHistoryRefModel::revision( size_t index ) const {
|
||||
if ( index == 0 || index > mSourceIndexes.size() )
|
||||
return "HEAD";
|
||||
const auto& branch = mSource->branchRef( mSourceIndexes[index - 1] );
|
||||
switch ( branch.type ) {
|
||||
case Git::RefType::Head:
|
||||
return "refs/heads/" + branch.name;
|
||||
case Git::RefType::Remote:
|
||||
return "refs/remotes/" + branch.name;
|
||||
case Git::RefType::Tag:
|
||||
return "refs/tags/" + branch.name;
|
||||
default:
|
||||
return "HEAD";
|
||||
}
|
||||
}
|
||||
|
||||
bool GitHistoryRefModel::isRevision( size_t index, std::string_view revision ) const {
|
||||
if ( index == 0 || index > mSourceIndexes.size() )
|
||||
return revision == "HEAD";
|
||||
const auto& branch = mSource->branchRef( mSourceIndexes[index - 1] );
|
||||
std::string_view prefix;
|
||||
switch ( branch.type ) {
|
||||
case Git::RefType::Head:
|
||||
prefix = "refs/heads/";
|
||||
break;
|
||||
case Git::RefType::Remote:
|
||||
prefix = "refs/remotes/";
|
||||
break;
|
||||
case Git::RefType::Tag:
|
||||
prefix = "refs/tags/";
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return revision.size() == prefix.size() + branch.name.size() &&
|
||||
revision.starts_with( prefix ) && revision.substr( prefix.size() ) == branch.name;
|
||||
}
|
||||
|
||||
void GitPlugin::registerSettings( SettingsPage& page ) {
|
||||
page.addGroup( i18n( "general", "General" ) );
|
||||
page.addText( "ui-refresh-frequency", "/config/ui_refresh_frequency",
|
||||
@@ -376,10 +444,8 @@ void GitPlugin::endModelStyler() {
|
||||
void GitPlugin::updateUINow( bool force ) {
|
||||
if ( !mGit || !getUISceneNode() )
|
||||
return;
|
||||
mLifetime.setDispatcher( getUISceneNode() );
|
||||
|
||||
if ( !mProjectPath.empty() )
|
||||
getUISceneNode()->runOnMainThread( [this] { buildSidePanelTab(); } );
|
||||
mLifetime.weakHandle().run( []( GitPlugin* plugin ) { plugin->buildSidePanelTab(); } );
|
||||
|
||||
updateStatus( force );
|
||||
updateBranches();
|
||||
@@ -389,7 +455,10 @@ void GitPlugin::updateUI() {
|
||||
if ( !mGit || !getUISceneNode() )
|
||||
return;
|
||||
|
||||
getUISceneNode()->debounce( [this] { updateUINow(); }, mRefreshFreq, GIT_STATUS_UPDATE_TAG );
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
getUISceneNode()->debounce(
|
||||
[lifetime] { lifetime.run( []( GitPlugin* plugin ) { plugin->updateUINow(); } ); },
|
||||
mRefreshFreq, GIT_STATUS_UPDATE_TAG );
|
||||
}
|
||||
|
||||
void GitPlugin::updateStatusBarSync() {
|
||||
@@ -658,8 +727,6 @@ void GitPlugin::updateStatus( bool force ) {
|
||||
}
|
||||
|
||||
PluginRequestHandle GitPlugin::processMessage( const PluginMessage& msg ) {
|
||||
if ( getUISceneNode() )
|
||||
mLifetime.setDispatcher( getUISceneNode() );
|
||||
switch ( msg.type ) {
|
||||
case PluginMessageType::WorkspaceFolderChanged: {
|
||||
if ( mGit ) {
|
||||
@@ -701,9 +768,9 @@ PluginRequestHandle GitPlugin::processMessage( const PluginMessage& msg ) {
|
||||
}
|
||||
|
||||
if ( getUISceneNode() && mSidePanel ) {
|
||||
getUISceneNode()->runOnMainThread( [this] {
|
||||
if ( mProjectPath.empty() ) {
|
||||
hideSidePanel();
|
||||
mLifetime.weakHandle().run( []( GitPlugin* plugin ) {
|
||||
if ( plugin->mProjectPath.empty() ) {
|
||||
plugin->hideSidePanel();
|
||||
}
|
||||
} );
|
||||
}
|
||||
@@ -717,6 +784,7 @@ PluginRequestHandle GitPlugin::processMessage( const PluginMessage& msg ) {
|
||||
break;
|
||||
}
|
||||
case ecode::PluginMessageType::UIReady: {
|
||||
mLifetime.setDispatcher( getUISceneNode() );
|
||||
if ( !mInitialized )
|
||||
updateUINow();
|
||||
if ( mModelStylerId == 0 )
|
||||
@@ -895,32 +963,34 @@ void GitPlugin::checkout( Git::Branch branch ) {
|
||||
if ( !mGit )
|
||||
return;
|
||||
|
||||
const auto checkOutFn = [this, branch]( bool createLocal ) {
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
const auto git = mGit;
|
||||
const std::string repo = repoSelected();
|
||||
const auto checkOutFn = [this, branch, lifetime, git, repo]( bool createLocal ) {
|
||||
mLoader->setVisible( true );
|
||||
mThreadPool->run( [this, branch, createLocal] {
|
||||
auto result =
|
||||
createLocal ? mGit->checkoutAndCreateLocalBranch( branch.name, "", repoSelected() )
|
||||
: mGit->checkout( branch.name, repoSelected() );
|
||||
if ( result.success() ) {
|
||||
{
|
||||
std::string repoSel = repoSelected();
|
||||
Lock l( mGitBranchMutex );
|
||||
mGitBranches[repoSel] = branch.name;
|
||||
}
|
||||
if ( mBranchesTree->getModel() ) {
|
||||
if ( createLocal )
|
||||
updateBranches();
|
||||
else
|
||||
mBranchesTree->getModel()->invalidate( Model::DontInvalidateIndexes );
|
||||
}
|
||||
} else {
|
||||
showMessage( LSPMessageType::Warning, result.result );
|
||||
}
|
||||
getUISceneNode()->runOnMainThread( [this, success = result.success()] {
|
||||
if ( success )
|
||||
invalidateHistory();
|
||||
mLoader->setVisible( false );
|
||||
} );
|
||||
mThreadPool->run( [branch, createLocal, lifetime, git, repo] {
|
||||
auto result = createLocal ? git->checkoutAndCreateLocalBranch( branch.name, "", repo )
|
||||
: git->checkout( branch.name, repo );
|
||||
lifetime.run(
|
||||
[branch, createLocal, repo, result = std::move( result )]( GitPlugin* plugin ) {
|
||||
if ( result.success() ) {
|
||||
{
|
||||
Lock l( plugin->mGitBranchMutex );
|
||||
plugin->mGitBranches[repo] = branch.name;
|
||||
}
|
||||
if ( plugin->mBranchesTree->getModel() ) {
|
||||
if ( createLocal )
|
||||
plugin->updateBranches();
|
||||
else
|
||||
plugin->mBranchesTree->getModel()->invalidate(
|
||||
Model::DontInvalidateIndexes );
|
||||
}
|
||||
plugin->invalidateHistory();
|
||||
} else {
|
||||
plugin->showMessage( LSPMessageType::Warning, result.result );
|
||||
}
|
||||
plugin->mLoader->setVisible( false );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
@@ -1352,8 +1422,9 @@ void GitPlugin::discard( const std::string& file ) {
|
||||
}
|
||||
|
||||
void GitPlugin::openFile( const std::string& file ) {
|
||||
getUISceneNode()->runOnMainThread( [this, file] {
|
||||
mManager->getLoadFileFn()( mGit->getProjectPath() + file, []( auto, auto ) {} );
|
||||
mLifetime.weakHandle().run( [file]( GitPlugin* plugin ) {
|
||||
plugin->mManager->getLoadFileFn()( plugin->mGit->getProjectPath() + file,
|
||||
[]( auto, auto ) {} );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -1956,13 +2027,13 @@ void GitPlugin::abortConflictOperation() {
|
||||
}
|
||||
|
||||
void GitPlugin::diff( const Git::DiffMode mode, const std::string& repoPath ) {
|
||||
mThreadPool->run( [this, mode, repoPath] {
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
mThreadPool->run( [this, mode, repoPath, lifetime] {
|
||||
auto res = mGit->diff( mode, repoPath );
|
||||
if ( res.fail() )
|
||||
return;
|
||||
|
||||
std::string repoName = this->repoName( repoPath );
|
||||
getUISceneNode()->runOnMainThread( [this, mode, res, repoName, repoPath] {
|
||||
lifetime.run( [mode, res = std::move( res ), repoPath]( GitPlugin* plugin ) {
|
||||
std::string modeName;
|
||||
switch ( mode ) {
|
||||
case Git::DiffHead: {
|
||||
@@ -1973,7 +2044,7 @@ void GitPlugin::diff( const Git::DiffMode mode, const std::string& repoPath ) {
|
||||
modeName = "staged";
|
||||
break;
|
||||
}
|
||||
getPluginContext()->loadDiffFromMemory(
|
||||
plugin->getPluginContext()->loadDiffFromMemory(
|
||||
res.result, UIDiffView::isMultiFileDiff( res.result ) ? modeName : "", "",
|
||||
repoPath );
|
||||
} );
|
||||
@@ -1981,11 +2052,12 @@ void GitPlugin::diff( const Git::DiffMode mode, const std::string& repoPath ) {
|
||||
}
|
||||
|
||||
void GitPlugin::diff( const std::string& file, Git::GitStatusType status ) {
|
||||
mThreadPool->run( [this, file, status] {
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
mThreadPool->run( [this, file, status, lifetime] {
|
||||
auto filePath = fixFilePath( file );
|
||||
if ( status == Git::GitStatusType::Untracked ) {
|
||||
getUISceneNode()->runOnMainThread( [this, filePath = std::move( filePath )] {
|
||||
getPluginContext()->loadDiffFromPaths( "", filePath );
|
||||
lifetime.run( [filePath = std::move( filePath )]( GitPlugin* plugin ) {
|
||||
plugin->getPluginContext()->loadDiffFromPaths( "", filePath );
|
||||
} );
|
||||
return;
|
||||
}
|
||||
@@ -2011,19 +2083,20 @@ void GitPlugin::diff( const std::string& file, Git::GitStatusType status ) {
|
||||
}
|
||||
}
|
||||
|
||||
getUISceneNode()->runOnMainThread(
|
||||
[this, result = std::move( result ), filePath = std::move( filePath ),
|
||||
oldImagePath = std::move( oldImagePath ), newImagePath = std::move( newImagePath )] {
|
||||
getPluginContext()->loadDiffFromMemory(
|
||||
result, newImagePath.empty() ? filePath : newImagePath, oldImagePath );
|
||||
} );
|
||||
lifetime.run( [result = std::move( result ), filePath = std::move( filePath ),
|
||||
oldImagePath = std::move( oldImagePath ),
|
||||
newImagePath = std::move( newImagePath )]( GitPlugin* plugin ) {
|
||||
plugin->getPluginContext()->loadDiffFromMemory(
|
||||
result, newImagePath.empty() ? filePath : newImagePath, oldImagePath );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
void GitPlugin::diff( std::vector<Git::DiffFile> files ) {
|
||||
if ( files.empty() )
|
||||
return;
|
||||
mThreadPool->run( [this, files = std::move( files )] {
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
mThreadPool->run( [this, files = std::move( files ), lifetime] {
|
||||
std::string patch;
|
||||
std::string repoPath;
|
||||
for ( const auto& file : files ) {
|
||||
@@ -2042,10 +2115,10 @@ void GitPlugin::diff( std::vector<Git::DiffFile> files ) {
|
||||
if ( !patch.empty() && patch.back() != '\n' )
|
||||
patch += '\n';
|
||||
}
|
||||
getUISceneNode()->runOnMainThread(
|
||||
[this, patch = std::move( patch ), repoPath = std::move( repoPath )] {
|
||||
getPluginContext()->loadDiffFromMemory( patch, "selected files", "", repoPath );
|
||||
} );
|
||||
lifetime.run( [patch = std::move( patch ),
|
||||
repoPath = std::move( repoPath )]( GitPlugin* plugin ) {
|
||||
plugin->getPluginContext()->loadDiffFromMemory( patch, "selected files", "", repoPath );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -2253,17 +2326,55 @@ void GitPlugin::invalidateHistory() {
|
||||
}
|
||||
|
||||
void GitPlugin::updateHistoryHeader() {
|
||||
if ( !mHistoryRefText )
|
||||
if ( !mHistoryRefDropDown || !mHistoryRefModel )
|
||||
return;
|
||||
std::string branch;
|
||||
auto rowCount = mHistoryRefModel->rowCount();
|
||||
for ( size_t i = 0; i < rowCount; ++i ) {
|
||||
if ( mHistoryRefModel->isRevision( i, mHistoryRevision ) ) {
|
||||
mUpdatingHistoryRefs = true;
|
||||
mHistoryRefDropDown->getListView()->getSelection().set(
|
||||
mHistoryRefModel->index( i, 0 ) );
|
||||
mUpdatingHistoryRefs = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GitPlugin::updateHistoryRefs( const std::shared_ptr<GitBranchModel>& model ) {
|
||||
if ( !mHistoryRefDropDown )
|
||||
return;
|
||||
std::string currentBranch;
|
||||
{
|
||||
const std::string repo = repoSelected();
|
||||
Lock l( mGitBranchMutex );
|
||||
auto it = mGitBranches.find( repo );
|
||||
if ( it != mGitBranches.end() )
|
||||
branch = it->second;
|
||||
currentBranch = it->second;
|
||||
}
|
||||
mHistoryRefText->setText( branch.empty() ? "HEAD" : branch );
|
||||
String headLabel{ "HEAD" };
|
||||
if ( !currentBranch.empty() )
|
||||
headLabel = String::fromUtf8( String::format( "HEAD · %s", currentBranch ) );
|
||||
auto historyRefModel = std::make_shared<GitHistoryRefModel>( model, std::move( headLabel ) );
|
||||
size_t selected = 0;
|
||||
bool found = false;
|
||||
for ( size_t i = 0; i < historyRefModel->rowCount(); ++i ) {
|
||||
if ( historyRefModel->isRevision( i, mHistoryRevision ) ) {
|
||||
selected = i;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const bool revisionChanged = !found && mHistoryRevision != "HEAD";
|
||||
if ( !found )
|
||||
mHistoryRevision = "HEAD";
|
||||
mUpdatingHistoryRefs = true;
|
||||
mHistoryRefModel = std::move( historyRefModel );
|
||||
mHistoryRefDropDown->setModel( mHistoryRefModel );
|
||||
mHistoryRefDropDown->getListView()->getSelection().set(
|
||||
mHistoryRefModel->index( selected, 0 ) );
|
||||
mUpdatingHistoryRefs = false;
|
||||
if ( revisionChanged )
|
||||
invalidateHistory();
|
||||
}
|
||||
|
||||
void GitPlugin::ensureHistoryLoaded() {
|
||||
@@ -2288,6 +2399,7 @@ void GitPlugin::reloadHistory() {
|
||||
mHistoryTree->clearViewMetadata();
|
||||
mHistoryModel->setRootLoading();
|
||||
Git::HistoryQuery query;
|
||||
query.revision = mHistoryRevision;
|
||||
auto git = mGit;
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
++mRunningHistoryRequests;
|
||||
@@ -2401,7 +2513,8 @@ void GitPlugin::openCommitDetails( const Git::Commit& commit ) {
|
||||
</hbox>
|
||||
<hbox lw="mp" lh="wc" margin-top="8dp">
|
||||
<TextView id="git_commit_subject" lw="0" lw8="1" lh="wc"
|
||||
font-size="14dp" word-wrap="true" focusable="false" />
|
||||
font-size="14dp" word-wrap="false" text-overflow="ellipsis"
|
||||
focusable="false" />
|
||||
<PushButton id="git_commit_message_toggle"
|
||||
text="@string(git_expand_commit_description, Expand Commit Description)"
|
||||
icon="icon(unfold, 12dp)" text-as-fallback="true"
|
||||
@@ -2579,11 +2692,19 @@ void GitPlugin::loadCommitFiles() {
|
||||
( result.result.empty() ? "" : ": " + result.result ) );
|
||||
return;
|
||||
}
|
||||
std::string body = std::move( result.message );
|
||||
if ( body.compare( 0, commit.subject.size(), commit.subject ) == 0 ) {
|
||||
body.erase( 0, commit.subject.size() );
|
||||
while ( !body.empty() && ( body.front() == '\n' || body.front() == '\r' ) )
|
||||
body.erase( body.begin() );
|
||||
std::string message = std::move( result.message );
|
||||
const size_t subjectEnd = message.find_first_of( "\r\n" );
|
||||
const std::string_view subject{
|
||||
message.data(), subjectEnd == std::string::npos ? message.size() : subjectEnd };
|
||||
plugin->mCommitDetailsSubject->setText(
|
||||
String::fromUtf8( subject.empty() ? commit.subject : std::string{ subject } ) );
|
||||
std::string body;
|
||||
if ( subjectEnd != std::string::npos ) {
|
||||
size_t bodyStart = subjectEnd;
|
||||
while ( bodyStart < message.size() &&
|
||||
( message[bodyStart] == '\n' || message[bodyStart] == '\r' ) )
|
||||
++bodyStart;
|
||||
body = message.substr( bodyStart );
|
||||
}
|
||||
plugin->mCommitDetailsMessageBody = std::move( body );
|
||||
plugin->mCommitDetailsMessage->setText(
|
||||
@@ -2638,20 +2759,26 @@ void GitPlugin::updateBranches( bool force ) {
|
||||
return;
|
||||
|
||||
if ( !mGit || mGit->getGitFolder().empty() ) {
|
||||
getUISceneNode()->runOnMainThread( [this] { updateBranchesUI( nullptr ); } );
|
||||
mLifetime.weakHandle().run(
|
||||
[]( GitPlugin* plugin ) { plugin->updateBranchesUI( nullptr ); } );
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string requestedRepo = repoSelected();
|
||||
const auto lifetime = mLifetime.weakHandle();
|
||||
mRunningUpdateBranches++;
|
||||
mThreadPool->run(
|
||||
[this] {
|
||||
[this, requestedRepo, lifetime] {
|
||||
if ( !mGit || mGit->getGitFolder().empty() ) {
|
||||
getUISceneNode()->runOnMainThread( [this] { updateBranchesUI( nullptr ); } );
|
||||
lifetime.run( [requestedRepo]( GitPlugin* plugin ) {
|
||||
if ( requestedRepo == plugin->repoSelected() )
|
||||
plugin->updateBranchesUI( nullptr );
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
auto prevBranch = updateReposBranches();
|
||||
auto branches = mGit->getAllBranchesAndTags( Git::RefType::All, {}, repoSelected() );
|
||||
auto branches = mGit->getAllBranchesAndTags( Git::RefType::All, {}, requestedRepo );
|
||||
auto hash = GitBranchModel::hashBranches( branches );
|
||||
auto model = GitBranchModel::asModel( std::move( branches ), hash, this );
|
||||
|
||||
@@ -2660,23 +2787,26 @@ void GitPlugin::updateBranches( bool force ) {
|
||||
Lock l( mGitBranchMutex );
|
||||
branchChanged = prevBranch != mGitBranches;
|
||||
}
|
||||
if ( mBranchesTree && mBranchesTree->getModel() &&
|
||||
static_cast<GitBranchModel*>( mBranchesTree->getModel() )->getHash() == hash ) {
|
||||
if ( branchChanged ) {
|
||||
getUISceneNode()->runOnMainThread( [this] {
|
||||
if ( mBranchesTree && mBranchesTree->getModel() )
|
||||
mBranchesTree->getModel()->invalidate( Model::DontInvalidateIndexes );
|
||||
updateHistoryHeader();
|
||||
invalidateHistory();
|
||||
} );
|
||||
lifetime.run( [model, hash, branchChanged, requestedRepo]( GitPlugin* plugin ) {
|
||||
if ( requestedRepo != plugin->repoSelected() )
|
||||
return;
|
||||
if ( plugin->mBranchesTree && plugin->mBranchesTree->getModel() &&
|
||||
static_cast<GitBranchModel*>( plugin->mBranchesTree->getModel() )->getHash() ==
|
||||
hash ) {
|
||||
if ( plugin->mBranchesTree->getModel() ) {
|
||||
if ( branchChanged )
|
||||
plugin->mBranchesTree->getModel()->invalidate(
|
||||
Model::DontInvalidateIndexes );
|
||||
plugin->updateHistoryRefs( std::static_pointer_cast<GitBranchModel>(
|
||||
plugin->mBranchesTree->getModelShared() ) );
|
||||
}
|
||||
if ( branchChanged )
|
||||
plugin->invalidateHistory();
|
||||
} else {
|
||||
plugin->updateBranchesUI( model );
|
||||
if ( branchChanged )
|
||||
plugin->invalidateHistory();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
getUISceneNode()->runOnMainThread( [this, model, branchChanged] {
|
||||
updateBranchesUI( model );
|
||||
if ( branchChanged )
|
||||
invalidateHistory();
|
||||
} );
|
||||
},
|
||||
[this]( auto ) { mRunningUpdateBranches--; } );
|
||||
@@ -2706,7 +2836,6 @@ void GitPlugin::updateRepos() {
|
||||
|
||||
void GitPlugin::updateBranchesUI( std::shared_ptr<GitBranchModel> model ) {
|
||||
buildSidePanelTab();
|
||||
updateHistoryHeader();
|
||||
|
||||
if ( !model ) {
|
||||
mBranchesTree->setModel( model );
|
||||
@@ -2716,6 +2845,7 @@ void GitPlugin::updateBranchesUI( std::shared_ptr<GitBranchModel> model ) {
|
||||
mBranchesTree->expandAll();
|
||||
}
|
||||
|
||||
updateHistoryRefs( model );
|
||||
updateRepos();
|
||||
|
||||
std::vector<String> items;
|
||||
@@ -2838,7 +2968,7 @@ void GitPlugin::buildSidePanelTab() {
|
||||
</vbox>
|
||||
<vbox id="git_history" lw="mp" lh="mp">
|
||||
<hbox lw="mp" lh="wc" padding="4dp">
|
||||
<TextView id="git_history_ref" text="HEAD" lw="0" lw8="1" lh="wc" layout_gravity="center_vertical" text-overflow="ellipsis" />
|
||||
<DropDownModelList id="git_history_ref" lw="0" lw8="1" lh="wc" menu-width-mode="expand-if-needed" />
|
||||
<PushButton id="git_history_refresh" text="@string(git_history_refresh, Refresh)" icon="icon(refresh, 12dp)" text-as-fallback="true" />
|
||||
</hbox>
|
||||
<GitHistoryTreeView id="git_history_tree" lw="mp" lh="0" lw8="1" />
|
||||
@@ -2868,7 +2998,7 @@ void GitPlugin::buildSidePanelTab() {
|
||||
mTabContents->bind( "git_branches_tree", mBranchesTree );
|
||||
mTabContents->bind( "git_status_tree", mStatusTree );
|
||||
mTabContents->bind( "git_history_tree", mHistoryTree );
|
||||
mTabContents->bind( "git_history_ref", mHistoryRefText );
|
||||
mTabContents->bind( "git_history_ref", mHistoryRefDropDown );
|
||||
mTabContents->bind( "git_content", mGitContentView );
|
||||
mTabContents->bind( "git_no_content", mGitNoContentView );
|
||||
mTabContents->bind( "git_conflict_state", mConflictStateBar );
|
||||
@@ -2880,7 +3010,24 @@ void GitPlugin::buildSidePanelTab() {
|
||||
mTabContents->find( "branch_push" )->onClick( [this]( auto ) { push( repoSelected() ); } );
|
||||
mTabContents->find( "branch_add" )->onClick( [this]( auto ) { branchCreate(); } );
|
||||
mTabContents->find( "git_history_refresh" )->onClick( [this]( auto ) { reloadHistory(); } );
|
||||
updateHistoryHeader();
|
||||
mHistoryRefDropDown->getListView()->setColumnsVisible( { 0 } );
|
||||
mHistoryRefDropDown->getListView()->setAutoExpandOnSingleColumn( true );
|
||||
mHistoryRefDropDown->on( Event::OnItemSelected, [this]( const Event* ) {
|
||||
if ( mUpdatingHistoryRefs )
|
||||
return;
|
||||
const ModelIndex selected = mHistoryRefDropDown->getListView()->getSelection().first();
|
||||
if ( !selected.isValid() || !mHistoryRefModel )
|
||||
return;
|
||||
const std::string revision = mHistoryRefModel->revision( selected.row() );
|
||||
if ( revision == mHistoryRevision )
|
||||
return;
|
||||
mHistoryRevision = revision;
|
||||
invalidateHistory();
|
||||
} );
|
||||
updateHistoryRefs(
|
||||
mBranchesTree && mBranchesTree->getModel()
|
||||
? std::static_pointer_cast<GitBranchModel>( mBranchesTree->getModelShared() )
|
||||
: nullptr );
|
||||
mTabContents->find( "git_conflict_continue" )->onClick( [this]( auto ) {
|
||||
continueConflictOperation();
|
||||
} );
|
||||
@@ -2954,8 +3101,12 @@ void GitPlugin::buildSidePanelTab() {
|
||||
|
||||
mPanelSwicher->on( Event::OnItemSelected, [this, listBox]( const Event* ) {
|
||||
mStackWidget->setActiveWidget( mStackMap[listBox->getItemSelectedIndex()] );
|
||||
if ( listBox->getItemSelectedIndex() == 2 )
|
||||
if ( listBox->getItemSelectedIndex() == 2 ) {
|
||||
if ( mBranchesTree && mBranchesTree->getModel() )
|
||||
updateHistoryRefs(
|
||||
std::static_pointer_cast<GitBranchModel>( mBranchesTree->getModelShared() ) );
|
||||
ensureHistoryLoaded();
|
||||
}
|
||||
} );
|
||||
listBox->setSelected( 0 );
|
||||
mStackWidget->setActiveWidget( mStackMap[0] );
|
||||
@@ -3178,6 +3329,8 @@ void GitPlugin::buildSidePanelTab() {
|
||||
Lock l( mRepoMutex );
|
||||
mRepoSelected = repo.first;
|
||||
}
|
||||
mHistoryRevision = "HEAD";
|
||||
updateHistoryRefs( nullptr );
|
||||
invalidateHistory();
|
||||
updateBranches( true );
|
||||
updateStatus( true );
|
||||
|
||||
@@ -20,6 +20,7 @@ using namespace EE::Scene;
|
||||
namespace EE::UI {
|
||||
class UITreeView;
|
||||
class UIDropDownList;
|
||||
class UIDropDownModelList;
|
||||
class UIStackWidget;
|
||||
class UIListBoxItem;
|
||||
class UIMenu;
|
||||
@@ -31,6 +32,28 @@ namespace ecode {
|
||||
class Git;
|
||||
class GitBranchModel;
|
||||
|
||||
class GitHistoryRefModel : public Model {
|
||||
public:
|
||||
explicit GitHistoryRefModel( std::shared_ptr<GitBranchModel> source, String headLabel );
|
||||
|
||||
size_t rowCount( const ModelIndex& = {} ) const override { return mSourceIndexes.size() + 1; }
|
||||
|
||||
size_t columnCount( const ModelIndex& = {} ) const override { return 1; }
|
||||
|
||||
ModelIndex index( int row, int column, const ModelIndex& parent = {} ) const override;
|
||||
|
||||
Variant data( const ModelIndex& index, ModelRole role = ModelRole::Display ) const override;
|
||||
|
||||
std::string revision( size_t index ) const;
|
||||
|
||||
bool isRevision( size_t index, std::string_view revision ) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<GitBranchModel> mSource;
|
||||
std::vector<ModelIndex> mSourceIndexes;
|
||||
String mHeadLabel;
|
||||
};
|
||||
|
||||
static constexpr const char* GIT_EMPTY = "";
|
||||
static constexpr const char* GIT_SUCCESS = "success";
|
||||
static constexpr const char* GIT_ERROR = "error";
|
||||
@@ -125,7 +148,8 @@ class GitPlugin : public PluginBase {
|
||||
UITreeView* mBranchesTree{ nullptr };
|
||||
UITreeView* mStatusTree{ nullptr };
|
||||
UITreeView* mHistoryTree{ nullptr };
|
||||
UITextView* mHistoryRefText{ nullptr };
|
||||
UIDropDownModelList* mHistoryRefDropDown{ nullptr };
|
||||
std::shared_ptr<GitHistoryRefModel> mHistoryRefModel;
|
||||
std::shared_ptr<GitHistoryModel> mHistoryModel;
|
||||
UIDropDownList* mPanelSwicher{ nullptr };
|
||||
UIDropDownList* mRepoDropDown{ nullptr };
|
||||
@@ -140,6 +164,8 @@ class GitPlugin : public PluginBase {
|
||||
std::atomic<int> mRunningHistoryRequests{ 0 };
|
||||
std::atomic<Uint64> mHistoryGeneration{ 0 };
|
||||
std::string mHistoryRepo;
|
||||
std::string mHistoryRevision{ "HEAD" };
|
||||
bool mUpdatingHistoryRefs{ false };
|
||||
bool mHistoryLoaded{ false };
|
||||
UIWidget* mCommitDetailsView{ nullptr };
|
||||
UITextView* mCommitDetailsSubject{ nullptr };
|
||||
@@ -286,6 +312,8 @@ class GitPlugin : public PluginBase {
|
||||
|
||||
void updateHistoryHeader();
|
||||
|
||||
void updateHistoryRefs( const std::shared_ptr<GitBranchModel>& model );
|
||||
|
||||
void reloadHistory();
|
||||
|
||||
void invalidateHistory();
|
||||
|
||||
@@ -283,7 +283,7 @@ Plugin* LSPClientPlugin::NewSync( PluginManager* pluginManager ) {
|
||||
}
|
||||
|
||||
LSPClientPlugin::LSPClientPlugin( PluginManager* pluginManager, bool sync ) :
|
||||
Plugin( pluginManager ) {
|
||||
Plugin( pluginManager ), mLifetime( this, getUISceneNode() ) {
|
||||
if ( sync ) {
|
||||
load( pluginManager );
|
||||
} else {
|
||||
@@ -292,6 +292,7 @@ LSPClientPlugin::LSPClientPlugin( PluginManager* pluginManager, bool sync ) :
|
||||
}
|
||||
|
||||
LSPClientPlugin::~LSPClientPlugin() {
|
||||
mLifetime.invalidate();
|
||||
waitUntilLoaded();
|
||||
mShuttingDown = true;
|
||||
mManager->unsubscribeMessages( this );
|
||||
@@ -408,9 +409,11 @@ PluginRequestHandle LSPClientPlugin::processDocumentFormatting( const PluginMess
|
||||
|
||||
auto ret = server.server->documentFormatting(
|
||||
server.uri, msg.asJSON()["options"],
|
||||
[this, server]( const PluginIDType&, const std::vector<LSPTextEdit>& edits ) {
|
||||
mManager->getSplitter()->getUISceneNode()->runOnMainThread(
|
||||
[this, server, edits] { processDocumentFormattingResponse( server.uri, edits ); } );
|
||||
[lifetime = mLifetime.weakHandle(), server]( const PluginIDType&,
|
||||
const std::vector<LSPTextEdit>& edits ) {
|
||||
lifetime.run( [server, edits]( LSPClientPlugin* plugin ) {
|
||||
plugin->processDocumentFormattingResponse( server.uri, edits );
|
||||
} );
|
||||
} );
|
||||
|
||||
return ret;
|
||||
@@ -922,6 +925,7 @@ PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg )
|
||||
break;
|
||||
}
|
||||
case ecode::PluginMessageType::UIReady: {
|
||||
mLifetime.setDispatcher( getUISceneNode() );
|
||||
if ( mBrokenUserConfigFile )
|
||||
displayBrokenUserConfigFileWarning();
|
||||
break;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../pluginmanager.hpp"
|
||||
#include "lspclientservermanager.hpp"
|
||||
#include <eepp/config.hpp>
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <eepp/system/clock.hpp>
|
||||
#include <eepp/system/mutex.hpp>
|
||||
#include <eepp/system/sys.hpp>
|
||||
@@ -106,8 +107,10 @@ class LSPClientPlugin : public Plugin {
|
||||
const Float& fontSize );
|
||||
|
||||
protected:
|
||||
MainThreadLifetime<LSPClientPlugin> mLifetime;
|
||||
friend class LSPDocumentClient;
|
||||
friend class LSPClientServer;
|
||||
friend class LSPClientServerManager;
|
||||
|
||||
Clock mClock;
|
||||
Mutex mDocMutex;
|
||||
|
||||
@@ -203,7 +203,7 @@ void LSPClientServerManager::executeCommand( const std::shared_ptr<TextDocument>
|
||||
void LSPClientServerManager::applyWorkspaceEdit(
|
||||
const LSPWorkspaceEdit& edit,
|
||||
const std::function<void( const LSPApplyWorkspaceEditResponse& res )>& resCb ) {
|
||||
mPluginManager->getSplitter()->getUISceneNode()->runOnMainThread( [this, edit, resCb] {
|
||||
mPlugin->mLifetime.weakHandle().run( [this, edit, resCb]( LSPClientPlugin* ) {
|
||||
bool allDone = true;
|
||||
|
||||
for ( const auto& ed : edit.changes ) {
|
||||
@@ -279,10 +279,9 @@ void LSPClientServerManager::rangeFormatting( std::shared_ptr<TextDocument> doc
|
||||
server->documentRangeFormatting(
|
||||
uri, doc->getSelection(), getURIJSON( doc ),
|
||||
[this, uri]( const PluginIDType&, const std::vector<LSPTextEdit>& edits ) {
|
||||
mPluginManager->getSplitter()->getUISceneNode()->runOnMainThread(
|
||||
[this, edits, uri] {
|
||||
mPlugin->processDocumentFormattingResponse( uri, edits );
|
||||
} );
|
||||
mPlugin->mLifetime.weakHandle().run( [edits, uri]( LSPClientPlugin* plugin ) {
|
||||
plugin->processDocumentFormattingResponse( uri, edits );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
} );
|
||||
|
||||
@@ -19,11 +19,13 @@ ProjectDirectoryTree::ProjectDirectoryTree(
|
||||
mClosing( false ),
|
||||
mIgnoreMatcher( path ),
|
||||
mPluginManager( pluginManager ),
|
||||
mLoadFileFromPathOrFocusFn( std::move( loadFileFromPathOrFocusFn ) ) {
|
||||
mLoadFileFromPathOrFocusFn( std::move( loadFileFromPathOrFocusFn ) ),
|
||||
mLifetime( this, pluginManager ? pluginManager->getUISceneNode() : nullptr ) {
|
||||
FileSystem::dirAddSlashAtEnd( mPath );
|
||||
}
|
||||
|
||||
ProjectDirectoryTree::~ProjectDirectoryTree() {
|
||||
mLifetime.invalidate();
|
||||
mClosing = true;
|
||||
if ( mPluginManager )
|
||||
mPluginManager->unsubscribeMessages( "ProjectDirectoryTree" );
|
||||
@@ -691,8 +693,9 @@ PluginRequestHandle ProjectDirectoryTree::processMessage( const PluginMessage& m
|
||||
if ( !matchesMap.empty() ) {
|
||||
if ( mPluginManager && mLoadFileFromPathOrFocusFn ) {
|
||||
std::string filePath( matchesMap.begin()->second );
|
||||
mPluginManager->getUISceneNode()->runOnMainThread(
|
||||
[this, filePath]() { mLoadFileFromPathOrFocusFn( filePath ); } );
|
||||
mLifetime.weakHandle().run( [filePath]( ProjectDirectoryTree* tree ) {
|
||||
tree->mLoadFileFromPathOrFocusFn( filePath );
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "ignorematcher.hpp"
|
||||
#include "plugins/pluginmanager.hpp"
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <eepp/scene/scenemanager.hpp>
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
#include <eepp/system/mutex.hpp>
|
||||
@@ -189,6 +190,7 @@ class ProjectDirectoryTree {
|
||||
IgnoreMatcherManager mIgnoreMatcher;
|
||||
PluginManager* mPluginManager{ nullptr };
|
||||
std::function<void( const std::string& )> mLoadFileFromPathOrFocusFn;
|
||||
MainThreadLifetime<ProjectDirectoryTree> mLifetime;
|
||||
|
||||
void getDirectoryFiles( std::vector<std::string>& files, std::vector<std::string>& names,
|
||||
std::string directory, std::set<std::string> currentDirs,
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
namespace ecode {
|
||||
|
||||
SettingsActions::SettingsActions( App* app ) :
|
||||
mApp( app ), mLifetime( this, app ? app->getUISceneNode() : nullptr ) {}
|
||||
|
||||
void SettingsActions::checkForUpdatesResponse( Http::Response&& response, bool fromStartup ) {
|
||||
auto updatesError = [this, fromStartup]() {
|
||||
if ( fromStartup )
|
||||
@@ -97,13 +100,15 @@ void SettingsActions::checkForUpdatesResponse( Http::Response&& response, bool f
|
||||
}
|
||||
|
||||
void SettingsActions::checkForUpdates( bool fromStartup ) {
|
||||
mLifetime.setDispatcher( mApp->getUISceneNode() );
|
||||
Http::getAsync(
|
||||
[this, fromStartup]( const Http&, Http::Request&, Http::Response& response ) {
|
||||
if ( !SceneManager::isActive() )
|
||||
return;
|
||||
mApp->getUISceneNode()->runOnMainThread( [this, res = response, fromStartup]() mutable {
|
||||
checkForUpdatesResponse( std::move( res ), fromStartup );
|
||||
} );
|
||||
mLifetime.weakHandle().run(
|
||||
[res = response, fromStartup]( SettingsActions* actions ) mutable {
|
||||
actions->checkForUpdatesResponse( std::move( res ), fromStartup );
|
||||
} );
|
||||
},
|
||||
"https://api.github.com/repos/SpartanJ/ecode/releases/latest", Seconds( 30 ) );
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define SETTINGSACTIONS_HPP
|
||||
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
|
||||
namespace ecode {
|
||||
|
||||
@@ -9,7 +10,7 @@ class App;
|
||||
|
||||
class SettingsActions {
|
||||
public:
|
||||
explicit SettingsActions( App* app ) : mApp( app ) {}
|
||||
explicit SettingsActions( App* app );
|
||||
|
||||
void checkForUpdates( bool fromStartup = false );
|
||||
|
||||
@@ -29,6 +30,7 @@ class SettingsActions {
|
||||
|
||||
private:
|
||||
App* mApp{ nullptr };
|
||||
MainThreadLifetime<SettingsActions> mLifetime;
|
||||
|
||||
String i18n( const std::string& key, const String& def );
|
||||
|
||||
|
||||
@@ -308,7 +308,8 @@ static void disableTabFocusTree( Node* node ) {
|
||||
disableTabFocusTree( child );
|
||||
}
|
||||
|
||||
SettingsPanel::SettingsPanel( App* app ) : mApp( app ) {}
|
||||
SettingsPanel::SettingsPanel( App* app ) :
|
||||
mApp( app ), mLifetime( this, app ? app->getUISceneNode() : nullptr ) {}
|
||||
|
||||
SettingsPanel::PanelState& SettingsPanel::state( Scope scope ) {
|
||||
return scope == Scope::User ? mUser : mProject;
|
||||
@@ -2107,8 +2108,9 @@ void SettingsPanel::addPluginSettings( PanelState& panel ) {
|
||||
auto* window = panel.window;
|
||||
window->runOnMainThread( [this, window, path] {
|
||||
window->closeWindow();
|
||||
mApp->getUISceneNode()->runOnMainThread(
|
||||
[this, path] { mApp->focusOrLoadFile( path ); } );
|
||||
mLifetime.weakHandle().run( [path]( SettingsPanel* panel ) {
|
||||
panel->mApp->focusOrLoadFile( path );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
panel.documents.emplace_back( std::move( document ) );
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "settingsmodel.hpp"
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -59,6 +60,7 @@ class SettingsPanel {
|
||||
};
|
||||
|
||||
App* mApp{ nullptr };
|
||||
MainThreadLifetime<SettingsPanel> mLifetime;
|
||||
PanelState mUser;
|
||||
PanelState mProject;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace ecode {
|
||||
StatusBuildOutputController::StatusBuildOutputController( UISplitter* mainSplitter,
|
||||
UISceneNode* uiSceneNode,
|
||||
PluginContextProvider* pluginContext ) :
|
||||
StatusBarElement( mainSplitter, uiSceneNode, pluginContext ) {}
|
||||
StatusBarElement( mainSplitter, uiSceneNode, pluginContext ), mLifetime( this, uiSceneNode ) {}
|
||||
|
||||
static std::string getProjectOutputParserTypeToString( const ProjectOutputParserTypes& type ) {
|
||||
switch ( type ) {
|
||||
@@ -286,8 +286,8 @@ void StatusBuildOutputController::runBuild( const std::string& buildName,
|
||||
updateBuildButton();
|
||||
|
||||
if ( !mContext->getWindow()->hasFocus() ) {
|
||||
mContext->getUISceneNode()->runOnMainThread( [this] {
|
||||
mContext->getWindow()->flash( WindowFlashOperation::UntilFocused );
|
||||
mLifetime.weakHandle().run( []( StatusBuildOutputController* controller ) {
|
||||
controller->mContext->getWindow()->flash( WindowFlashOperation::UntilFocused );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -527,19 +527,25 @@ void StatusBuildOutputController::createContainer() {
|
||||
if ( !data.isValid() )
|
||||
return;
|
||||
std::string path = data.toString();
|
||||
mUISceneNode->runOnMainThread( [this, path, lineNum, colNum] {
|
||||
UITab* tab = mSplitter->isDocumentOpen( path );
|
||||
if ( !tab ) {
|
||||
mContext->loadFileFromPath(
|
||||
path, true, nullptr,
|
||||
[this, lineNum, colNum]( auto, auto ) {
|
||||
onLoadDone( lineNum, colNum );
|
||||
} );
|
||||
} else {
|
||||
tab->getTabWidget()->setTabSelected( tab );
|
||||
onLoadDone( lineNum, colNum );
|
||||
}
|
||||
} );
|
||||
mLifetime.weakHandle().run(
|
||||
[path, lineNum,
|
||||
colNum]( StatusBuildOutputController* controller ) {
|
||||
UITab* tab = controller->mSplitter->isDocumentOpen( path );
|
||||
if ( !tab ) {
|
||||
controller->mContext->loadFileFromPath(
|
||||
path, true, nullptr,
|
||||
[lifetime = controller->mLifetime.weakHandle(),
|
||||
lineNum, colNum]( auto, auto ) {
|
||||
lifetime.run(
|
||||
[lineNum, colNum]( auto* loaded ) {
|
||||
loaded->onLoadDone( lineNum, colNum );
|
||||
} );
|
||||
} );
|
||||
} else {
|
||||
tab->getTabWidget()->setTabSelected( tab );
|
||||
controller->onLoadDone( lineNum, colNum );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "projectbuild.hpp"
|
||||
#include "uistatusbar.hpp"
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <eepp/system/luapattern.hpp>
|
||||
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
|
||||
#include <eepp/ui/uicodeeditor.hpp>
|
||||
@@ -40,7 +41,7 @@ class StatusBuildOutputController : public StatusBarElement {
|
||||
StatusBuildOutputController( UISplitter* mainSplitter, UISceneNode* uiSceneNode,
|
||||
PluginContextProvider* pluginContext );
|
||||
|
||||
virtual ~StatusBuildOutputController() {};
|
||||
virtual ~StatusBuildOutputController() { mLifetime.invalidate(); };
|
||||
|
||||
void runBuild( const std::string& buildName, const std::string& buildType,
|
||||
const ProjectBuildOutputParser& outputParser = {}, bool isClean = false,
|
||||
@@ -73,6 +74,7 @@ class StatusBuildOutputController : public StatusBarElement {
|
||||
std::vector<PatternHolder> mPatternHolder;
|
||||
std::string mCurLineBuffer;
|
||||
bool mScrollLocked{ true };
|
||||
MainThreadLifetime<StatusBuildOutputController> mLifetime;
|
||||
|
||||
void createContainer();
|
||||
|
||||
|
||||
@@ -185,6 +185,7 @@ UniversalLocator::UniversalLocator( UICodeEditorSplitter* editorSplitter, UIScen
|
||||
App* app ) :
|
||||
mSplitter( editorSplitter ),
|
||||
mUISceneNode( sceneNode ),
|
||||
mLifetime( this, sceneNode ),
|
||||
mApp( app ),
|
||||
mCommandPalette( mApp->getThreadPool() ) {
|
||||
|
||||
@@ -466,11 +467,12 @@ void UniversalLocator::updateFilesTable( bool useGlob ) {
|
||||
: ProjectDirectoryTree::MatchType::Fuzzy,
|
||||
text, LOCATEBAR_MAX_RESULTS,
|
||||
[this, text]( auto res ) {
|
||||
mUISceneNode->runOnMainThread( [this, res] {
|
||||
mLocateTable->setModel( res );
|
||||
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
|
||||
mLocateTable->scrollToTop();
|
||||
updateLocateBarSync();
|
||||
mLifetime.weakHandle().run( [res]( UniversalLocator* locator ) {
|
||||
locator->mLocateTable->setModel( res );
|
||||
locator->mLocateTable->getSelection().set(
|
||||
locator->mLocateTable->getModel()->index( 0 ) );
|
||||
locator->mLocateTable->scrollToTop();
|
||||
locator->updateLocateBarSync();
|
||||
} );
|
||||
},
|
||||
mApp->getCurrentProject() );
|
||||
@@ -500,11 +502,12 @@ void UniversalLocator::updateCommandPaletteTable() {
|
||||
|
||||
if ( txt.size() > 1 ) {
|
||||
mCommandPalette.asyncFuzzyMatch( txt.substr( 1 ).trim(), 10000, [this]( auto res ) {
|
||||
mUISceneNode->runOnMainThread( [this, res] {
|
||||
mLocateTable->setModel( res );
|
||||
if ( mLocateTable->getModel()->hasChildren() )
|
||||
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
|
||||
mLocateTable->scrollToTop();
|
||||
mLifetime.weakHandle().run( [res]( UniversalLocator* locator ) {
|
||||
locator->mLocateTable->setModel( res );
|
||||
if ( locator->mLocateTable->getModel()->hasChildren() )
|
||||
locator->mLocateTable->getSelection().set(
|
||||
locator->mLocateTable->getModel()->index( 0 ) );
|
||||
locator->mLocateTable->scrollToTop();
|
||||
} );
|
||||
} );
|
||||
} else if ( mCommandPalette.getCurModel() ) {
|
||||
@@ -575,15 +578,17 @@ bool UniversalLocator::tryLocator( const String& txt ) {
|
||||
query.trim();
|
||||
const Uint64 generation = ++mLocatorModelGeneration;
|
||||
locator->modelFn( query, [this, generation]( std::shared_ptr<Model> model ) {
|
||||
mUISceneNode->runOnMainThread( [this, generation, model = std::move( model )] {
|
||||
if ( generation != mLocatorModelGeneration || !mLocateBarLayout->isVisible() )
|
||||
return;
|
||||
mLocateTable->setModel( model );
|
||||
if ( model && model->hasChildren() )
|
||||
mLocateTable->getSelection().set( model->index( 0 ) );
|
||||
mLocateTable->scrollToTop();
|
||||
updateLocateBarSync();
|
||||
} );
|
||||
mLifetime.weakHandle().run(
|
||||
[generation, model = std::move( model )]( UniversalLocator* locator ) {
|
||||
if ( generation != locator->mLocatorModelGeneration ||
|
||||
!locator->mLocateBarLayout->isVisible() )
|
||||
return;
|
||||
locator->mLocateTable->setModel( model );
|
||||
if ( model && model->hasChildren() )
|
||||
locator->mLocateTable->getSelection().set( model->index( 0 ) );
|
||||
locator->mLocateTable->scrollToTop();
|
||||
locator->updateLocateBarSync();
|
||||
} );
|
||||
} );
|
||||
return true;
|
||||
}
|
||||
@@ -1232,17 +1237,17 @@ void UniversalLocator::requestWorkspaceSymbol() {
|
||||
}
|
||||
|
||||
void UniversalLocator::updateWorkspaceSymbol( const LSPSymbolInformationList& res ) {
|
||||
mUISceneNode->runOnMainThread( [this, res] {
|
||||
if ( !mWorkspaceSymbolModel ) {
|
||||
mWorkspaceSymbolModel =
|
||||
LSPSymbolInfoModel::create( mApp->getUISceneNode(), mWorkspaceSymbolQuery, res );
|
||||
mLifetime.weakHandle().run( [res]( UniversalLocator* locator ) {
|
||||
if ( !locator->mWorkspaceSymbolModel ) {
|
||||
locator->mWorkspaceSymbolModel = LSPSymbolInfoModel::create(
|
||||
locator->mApp->getUISceneNode(), locator->mWorkspaceSymbolQuery, res );
|
||||
} else {
|
||||
mWorkspaceSymbolModel->setQuery( mWorkspaceSymbolQuery );
|
||||
mWorkspaceSymbolModel->append( res );
|
||||
locator->mWorkspaceSymbolModel->setQuery( locator->mWorkspaceSymbolQuery );
|
||||
locator->mWorkspaceSymbolModel->append( res );
|
||||
}
|
||||
mLocateTable->setModel( mWorkspaceSymbolModel );
|
||||
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
|
||||
mLocateTable->scrollToTop();
|
||||
locator->mLocateTable->setModel( locator->mWorkspaceSymbolModel );
|
||||
locator->mLocateTable->getSelection().set( locator->mLocateTable->getModel()->index( 0 ) );
|
||||
locator->mLocateTable->scrollToTop();
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -1293,10 +1298,11 @@ void UniversalLocator::updateDocumentSymbol( const LSPSymbolInformationList& res
|
||||
} else {
|
||||
asyncFuzzyMatchTextDocumentSymbol( res, mCurDocQuery, 100, [this]( const auto model ) {
|
||||
mTextDocumentSymbolModel = model;
|
||||
mUISceneNode->runOnMainThread( [this] {
|
||||
mLocateTable->setModel( mTextDocumentSymbolModel );
|
||||
mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) );
|
||||
mLocateTable->scrollToTop();
|
||||
mLifetime.weakHandle().run( []( UniversalLocator* locator ) {
|
||||
locator->mLocateTable->setModel( locator->mTextDocumentSymbolModel );
|
||||
locator->mLocateTable->getSelection().set(
|
||||
locator->mLocateTable->getModel()->index( 0 ) );
|
||||
locator->mLocateTable->scrollToTop();
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "projectdirectorytree.hpp"
|
||||
#include "widgetcommandexecuter.hpp"
|
||||
#include <eepp/ee.hpp>
|
||||
#include <eepp/scene/mainthreadlifetime.hpp>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
@@ -110,6 +111,7 @@ class UniversalLocator {
|
||||
UITextInput* mLocateInput{ nullptr };
|
||||
UICodeEditorSplitter* mSplitter{ nullptr };
|
||||
UISceneNode* mUISceneNode{ nullptr };
|
||||
MainThreadLifetime<UniversalLocator> mLifetime;
|
||||
std::shared_ptr<LSPSymbolInfoModel> mWorkspaceSymbolModel{ nullptr };
|
||||
std::string mWorkspaceSymbolQuery;
|
||||
App* mApp{ nullptr };
|
||||
|
||||
Reference in New Issue
Block a user