ecode: Project Build WIP.

This commit is contained in:
Martín Lucas Golini
2023-04-08 13:22:31 -03:00
parent 9eef0748f6
commit 1ba74ae338
11 changed files with 263 additions and 37 deletions

View File

@@ -27,9 +27,6 @@
"clear_sys_env": false,
"enabled": true
},
"env": {
"SHELL": "fish"
},
"os": [
"linux"
],
@@ -39,7 +36,29 @@
},
"error": [
{
"pattern": "([^:]*):(%d+):(%d+):%s?[%w%s]*error:%s?([^\n]*)",
"pattern": "([^:]+):(%d+):(%d+):%s?[%w%s]*error:%s?(.*)",
"pattern_order": {
"col": 3,
"file": 1,
"line": 2,
"message": 4
}
}
],
"notice": [
{
"pattern": "([^:]+):(%d+):(%d+):%s?[%w%s]*notice:%s?(.*)",
"pattern_order": {
"col": 3,
"file": 1,
"line": 2,
"message": 4
}
}
],
"warning": [
{
"pattern": "([^:]+):(%d+):(%d+):%s?[%w%s]*warning:%s?(.*)",
"pattern_order": {
"col": 3,
"file": 1,

View File

@@ -594,8 +594,13 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client {
Vector2f getRelativeScreenPosition( const TextPosition& pos );
bool getShowLinesRelativePosition() const;
void showLinesRelativePosition( bool showLinesRelativePosition );
UIScrollBar* getVScrollBar() const;
UIScrollBar* getHScrollBar() const;
protected:
struct LastXOffset {
TextPosition position{ 0, 0 };

View File

@@ -207,6 +207,8 @@ const SyntaxColorScheme::Style& SyntaxColorScheme::getSyntaxStyle( const std::st
return getSyntaxStyle( "symbol" );
else if ( type == "link" || type == "link_hover" )
return getSyntaxStyle( "function" );
else if ( type == "error" || type == "warning" || type == "notice" )
return getEditorSyntaxStyle( type );
else {
auto foundIt = mStyleCache.find( type );
if ( foundIt != mStyleCache.end() )

View File

@@ -1392,6 +1392,14 @@ void UICodeEditor::showLinesRelativePosition( bool showLinesRelativePosition ) {
mShowLinesRelativePosition = showLinesRelativePosition;
}
UIScrollBar* UICodeEditor::getVScrollBar() const {
return mVScrollBar;
}
UIScrollBar* UICodeEditor::getHScrollBar() const {
return mHScrollBar;
}
void UICodeEditor::drawCursor( const Vector2f& startScroll, const Float& lineHeight,
const TextPosition& cursor ) {
if ( mCursorVisible && !mLocked && isTextSelectionEnabled() ) {

View File

@@ -70,7 +70,10 @@ UISlider::UISlider( const std::string& tag, const UIOrientation& orientation ) :
mSlider->setDragEnabled( true );
mSlider->setSize( 4, 4 );
mSlider->setPosition( 0, 0 );
mSlider->addEventListener( Event::OnPositionChange, [&]( const Event* ) { fixSliderPos(); } );
mSlider->addEventListener( Event::OnPositionChange, [&]( const Event* ) {
if ( !mUpdating && !mOnPosChange )
fixSliderPos();
} );
if ( UIOrientation::Horizontal == mOrientation )
mSlider->centerVertical();

View File

@@ -748,6 +748,10 @@ void App::showStatusBar( bool show ) {
mStatusBar->setVisible( show );
}
ProjectBuildManager* App::getProjectBuildManager() const {
return mProjectBuildManager.get();
}
void App::switchSidePanel() {
mConfig.ui.showSidePanel = !mConfig.ui.showSidePanel;
mSettings->getWindowMenu()
@@ -1725,6 +1729,8 @@ std::vector<std::string> App::getUnlockedCommands() {
"open-locatebar",
"open-command-palette",
"open-global-search",
"project-build-start",
"project-build-cancel",
"toggle-locatebar",
"toggle-global-search",
"toggle-status-build-output",

View File

@@ -234,6 +234,17 @@ class App : public UICodeEditorSplitter::Client {
t.setCommand( "open-locatebar", [&] { mUniversalLocator->showLocateBar(); } );
t.setCommand( "toggle-locatebar", [&] { mUniversalLocator->toggleLocateBar(); } );
t.setCommand( "open-command-palette", [&] { mUniversalLocator->showCommandPalette(); } );
t.setCommand( "project-build-start", [&] {
if ( mProjectBuildManager && !mProjectBuildManager->isBuilding() ) {
mStatusBuildOutputController->run(
"ecode", "debug", mProjectBuildManager->getOutputParser( "ecode" ) );
}
} );
t.setCommand( "project-build-cancel", [&] {
if ( mProjectBuildManager && mProjectBuildManager->isBuilding() ) {
mProjectBuildManager->cancelBuild();
}
} );
t.setCommand( "open-workspace-symbol-search",
[&] { mUniversalLocator->showWorkspaceSymbol(); } );
t.setCommand( "open-document-symbol-search",
@@ -378,6 +389,8 @@ class App : public UICodeEditorSplitter::Client {
void showStatusBar( bool show );
ProjectBuildManager* getProjectBuildManager() const;
protected:
std::vector<std::string> mArgs;
EE::Window::Window* mWindow{ nullptr };

View File

@@ -59,17 +59,18 @@ ProjectBuildManager::ProjectBuildManager( const std::string& projectRoot,
ProjectBuildManager::~ProjectBuildManager() {
mShuttingDown = true;
mCancelBuild = true;
while ( mLoading )
Sys::sleep( Milliseconds( 0.1f ) );
while ( mBuilding )
Sys::sleep( Milliseconds( 0.1f ) );
}
ProjectBuildCommandsRes
ProjectBuildManager::run( const std::string& buildName,
std::function<String( const std::string&, const String& )> i18n,
const std::string& buildType, const ProjectBuildProgressFn& progressFn,
const ProjectBuildDoneFn& doneFn ) {
ProjectBuildCommandsRes ProjectBuildManager::run( const std::string& buildName,
const ProjectBuildi18nFn& i18n,
const std::string& buildType,
const ProjectBuildProgressFn& progressFn,
const ProjectBuildDoneFn& doneFn ) {
ProjectBuildCommandsRes res = generateBuildCommands( buildName, i18n, buildType );
if ( !res.isValid() )
return res;
@@ -78,7 +79,9 @@ ProjectBuildManager::run( const std::string& buildName,
return res;
}
mThreadPool->run( [this, res, progressFn, doneFn]() { runBuild( res, progressFn, doneFn ); } );
mThreadPool->run( [this, res, progressFn, doneFn, i18n, buildName, buildType]() {
runBuild( buildName, buildType, i18n, res, progressFn, doneFn );
} );
return res;
};
@@ -222,10 +225,9 @@ bool ProjectBuildManager::load() {
return true;
}
ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands(
const std::string& buildName,
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )> i18n,
const std::string& buildType ) {
ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands( const std::string& buildName,
const ProjectBuildi18nFn& i18n,
const std::string& buildType ) {
if ( !mLoaded )
return { i18n( "project_build_not_loaded", "No project build loaded!" ) };
@@ -261,17 +263,66 @@ ProjectBuildCommandsRes ProjectBuildManager::generateBuildCommands(
return res;
}
void ProjectBuildManager::runBuild( const ProjectBuildCommandsRes& res,
ProjectBuildOutputParser ProjectBuildManager::getOutputParser( const std::string& buildName ) {
auto buildIt = mBuilds.find( buildName );
if ( buildIt != mBuilds.end() )
return buildIt->second.mOutputParser;
return {};
}
void ProjectBuildManager::cancelBuild() {
mCancelBuild = true;
}
void ProjectBuildManager::runBuild( const std::string& buildName, const std::string& buildType,
const ProjectBuildi18nFn& i18n,
const ProjectBuildCommandsRes& res,
const ProjectBuildProgressFn& progressFn,
const ProjectBuildDoneFn& doneFn ) {
ScopedOp scopedOp( [this]() { mBuilding = true; }, [this]() { mBuilding = false; } );
Clock clock;
auto printElapsed = [&clock, &i18n, &progressFn]() {
if ( progressFn ) {
progressFn(
100, Sys::getDateTimeStr() + ": " +
String::format(
i18n( "build_elapsed_time", "Elapsed Time: %s.\n" ).toUtf8().c_str(),
clock.getElapsedTime().toString().c_str() ) );
}
};
if ( progressFn ) {
progressFn( 0, Sys::getDateTimeStr() + ": " +
String::format( i18n( "running_steps_for_project",
"Running steps for project %s...\n" )
.toUtf8()
.c_str(),
buildName.c_str() ) );
if ( !buildType.empty() )
progressFn(
0, Sys::getDateTimeStr() + ": " +
String::format(
i18n( "using_build_type", "Using build type: %s.\n" ).toUtf8().c_str(),
buildType.c_str() ) );
}
int c = 0;
for ( const auto& cmd : res.cmds ) {
int progress = c > 0 ? c / (Float)res.cmds.size() : 0;
Process process;
auto options = Process::SearchUserPath | Process::NoWindow | Process::CombinedStdoutStderr;
if ( !cmd.config.clearSysEnv )
options |= Process::InheritEnvironment;
if ( process.create( cmd.cmd, cmd.args, options, cmd.envs, cmd.workingDir ) ) {
if ( progressFn )
progressFn( progress,
Sys::getDateTimeStr() + ": " +
String::format(
i18n( "starting_process", "Starting %s %s\n" ).toUtf8().c_str(),
cmd.cmd.c_str(), cmd.args.c_str() ) );
std::string buffer( 1024, '\0' );
unsigned bytesRead = 0;
int returnCode;
@@ -279,30 +330,57 @@ void ProjectBuildManager::runBuild( const ProjectBuildCommandsRes& res,
bytesRead = process.readStdOut( buffer );
std::string data( buffer.substr( 0, bytesRead ) );
if ( progressFn )
progressFn( 50, std::move( data ) );
} while ( bytesRead != 0 && process.isAlive() && !mShuttingDown );
progressFn( progress, std::move( data ) );
} while ( bytesRead != 0 && process.isAlive() && !mShuttingDown && !mCancelBuild );
if ( mShuttingDown ) {
if ( mShuttingDown || mCancelBuild ) {
process.kill();
doneFn( EXIT_FAILURE );
mCancelBuild = false;
printElapsed();
if ( doneFn )
doneFn( EXIT_FAILURE );
return;
}
if ( progressFn )
progressFn( 90, {} );
process.join( &returnCode );
process.destroy();
if ( doneFn && returnCode != EXIT_SUCCESS ) {
progressFn( 100, {} );
doneFn( returnCode );
if ( returnCode != EXIT_SUCCESS ) {
if ( progressFn ) {
progressFn( 100,
String::format( i18n( "process_exited_with_errors",
"The process \"%s\" exited with errors.\n" )
.toUtf8()
.c_str(),
cmd.cmd.c_str() ) );
}
printElapsed();
if ( doneFn )
doneFn( returnCode );
return;
} else {
if ( progressFn ) {
progressFn( progress,
String::format( i18n( "process_exited_normally",
"The process \"%s\" exited normally.\n" )
.toUtf8()
.c_str(),
cmd.cmd.c_str() ) );
}
}
} else {
printElapsed();
if ( doneFn )
doneFn( EXIT_FAILURE );
return;
}
c++;
}
doneFn( EXIT_SUCCESS );
printElapsed();
if ( doneFn )
doneFn( EXIT_SUCCESS );
}
} // namespace ecode

View File

@@ -100,6 +100,11 @@ struct ProjectBuildOutputParserConfig {
};
class ProjectBuildOutputParser {
public:
const std::vector<ProjectBuildOutputParserConfig>& getConfig() const { return mConfig; }
bool useRelativeFilePaths() const { return mRelativeFilePaths; }
protected:
friend class ProjectBuildManager;
@@ -160,6 +165,8 @@ struct ProjectBuildCommandsRes {
using ProjectBuildProgressFn = std::function<void( int curProgress, std::string buffer )>;
using ProjectBuildDoneFn = std::function<void( int exitCode )>;
using ProjectBuildi18nFn =
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )>;
class ProjectBuildManager {
public:
@@ -167,16 +174,16 @@ class ProjectBuildManager {
~ProjectBuildManager();
ProjectBuildCommandsRes
run( const std::string& buildName,
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )> i18n,
const std::string& buildType = "", const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );
ProjectBuildCommandsRes run( const std::string& buildName, const ProjectBuildi18nFn& i18n,
const std::string& buildType = "",
const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );
ProjectBuildCommandsRes generateBuildCommands(
const std::string& buildName,
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )> i18n,
const std::string& buildType = "" );
ProjectBuildCommandsRes generateBuildCommands( const std::string& buildName,
const ProjectBuildi18nFn& i18n,
const std::string& buildType = "" );
ProjectBuildOutputParser getOutputParser( const std::string& buildName );
const ProjectBuildMap& getBuilds() const { return mBuilds; }
@@ -190,6 +197,8 @@ class ProjectBuildManager {
bool isBuilding() const { return mBuilding; }
void cancelBuild();
protected:
std::string mProjectRoot;
std::string mProjectFile;
@@ -199,8 +208,10 @@ class ProjectBuildManager {
bool mLoading{ false };
bool mBuilding{ false };
bool mShuttingDown{ false };
bool mCancelBuild{ false };
void runBuild( const ProjectBuildCommandsRes& res,
void runBuild( const std::string& buildName, const std::string& buildType,
const ProjectBuildi18nFn& i18n, const ProjectBuildCommandsRes& res,
const ProjectBuildProgressFn& progressFn = {},
const ProjectBuildDoneFn& doneFn = {} );

View File

@@ -60,6 +60,83 @@ void StatusBuildOutputController::show() {
}
}
static std::string getProjectOutputParserTypeToString( const ProjectOutputParserTypes& type ) {
switch ( type ) {
case ProjectOutputParserTypes::Error:
return "error";
case ProjectOutputParserTypes::Warning:
return "warning";
case ProjectOutputParserTypes::Notice:
return "notice";
}
return "notice";
}
void StatusBuildOutputController::run( const std::string& buildName, const std::string& buildType,
const ProjectBuildOutputParser& outputParser ) {
if ( !mApp->getProjectBuildManager() )
return;
auto pbm = mApp->getProjectBuildManager();
show();
mContainer->getDocument().reset();
mContainer->setScrollY( mContainer->getMaxScroll().y );
std::vector<SyntaxPattern> patterns;
for ( const auto& parser : outputParser.getConfig() ) {
SyntaxPattern ptn( { parser.pattern }, getProjectOutputParserTypeToString( parser.type ) );
patterns.emplace_back( std::move( ptn ) );
}
patterns.emplace_back(
SyntaxPattern( { "%d%d%d%d%-%d%d%-%d%d%s%d%d%:%d%d%:%d%d%:.*error.*[^\n]+" }, "error" ) );
patterns.emplace_back(
SyntaxPattern( { "%d%d%d%d%-%d%d%-%d%d%s%d%d%:%d%d%:%d%d%:.*warning.*[^\n]+" }, "error" ) );
patterns.emplace_back(
SyntaxPattern( { "%d%d%d%d%-%d%d%-%d%d%s%d%d%:%d%d%:%d%d%:[^\n]+" }, "notice" ) );
SyntaxDefinition synDef( "custom_build", {}, patterns );
mContainer->getDocument().setSyntaxDefinition( synDef );
mContainer->getVScrollBar()->setValue( 1.f );
auto res = pbm->run(
buildName, [this]( const auto& key, const auto& def ) { return mApp->i18n( key, def ); },
buildType,
[this]( auto, auto buffer ) {
mContainer->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mContainer->getVScrollBar()->getValue() == 1.f;
mContainer->getDocument().textInput( buffer );
if ( scrollToBottom )
mContainer->setScrollY( mContainer->getMaxScroll().y );
} );
},
[this]( auto exitCode ) {
String buffer;
if ( EXIT_SUCCESS == exitCode ) {
buffer = Sys::getDateTimeStr() + ": " +
mApp->i18n( "build_successful", "Build run successfully\n" );
} else {
buffer = Sys::getDateTimeStr() + ": " +
mApp->i18n( "build_failed", "Build run with errors\n" );
}
mContainer->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mContainer->getVScrollBar()->getValue() == 1.f;
mContainer->getDocument().textInput( buffer );
if ( scrollToBottom )
mContainer->setScrollY( mContainer->getMaxScroll().y );
} );
} );
if ( !res.isValid() ) {
mApp->getNotificationCenter()->addNotification( res.errorMsg );
}
}
UICodeEditor* StatusBuildOutputController::createContainer() {
UICodeEditor* editor = UICodeEditor::NewOpt( true, true );
editor->setLocked( true );

View File

@@ -1,6 +1,7 @@
#ifndef ECODE_STATUSBUILDOUTPUTCONTROLLER_HPP
#define ECODE_STATUSBUILDOUTPUTCONTROLLER_HPP
#include "projectbuild.hpp"
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include <eepp/ui/uiscenenode.hpp>
@@ -24,6 +25,9 @@ class StatusBuildOutputController {
void show();
void run( const std::string& buildName, const std::string& buildType,
const ProjectBuildOutputParser& outputParser = {} );
protected:
UISplitter* mMainSplitter{ nullptr };
UISceneNode* mUISceneNode{ nullptr };