eepp: Allow to unselect selection from UIAbstractView.

ecode: Updated icons ttf.
Completed implementation of Build Output, now it will list Issues found when compiling in the Issues section inside Build.
Tentative fix in LSPDocumentClient when parsing semantic highlight.
Improved Features Health GUI.
This commit is contained in:
Martín Lucas Golini
2023-06-11 17:23:18 -03:00
parent a1b173818e
commit de4875b255
16 changed files with 518 additions and 181 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -43,9 +43,9 @@
--highlight-primary: #FFFFFF7A;
--disabled-color: #727679;
--disabled-border: #43474c;
--theme-error: #cc0000;
--theme-warning: #cccc00;
--theme-success: #00cc00;
--theme-error: #ff4040;
--theme-warning: #ffff40;
--theme-success: #40ff40;
droppable-hovering-color: #FFFFFF20;
}
@@ -771,6 +771,7 @@ Menu::RadioButton::shortcut {
color: var(--menu-font);
}
PushButton:disabled > PushButton::icon,
Menu::Item:disabled > Menu::Item::text,
Menu::Item:disabled > Menu::Item::shortcut,
Menu::Item:disabled > Menu::Item::icon,

View File

@@ -48,6 +48,8 @@ class EE_API FileInfo {
std::string linksTo() const;
std::string getRealPath() const;
bool exists() const;
void getInfo();

View File

@@ -11,8 +11,9 @@ class EE_API UIWidgetCreator {
typedef std::function<UIWidget*( std::string )> CustomWidgetCb;
typedef std::function<UIWidget*()> RegisterWidgetCb;
typedef std::map<std::string, UIWidgetCreator::CustomWidgetCb> WidgetCallbackMap;
typedef std::map<std::string, UIWidgetCreator::RegisterWidgetCb> RegisteredWidgetCallbackMap;
typedef std::unordered_map<std::string, UIWidgetCreator::CustomWidgetCb> WidgetCallbackMap;
typedef std::unordered_map<std::string, UIWidgetCreator::RegisterWidgetCb>
RegisteredWidgetCallbackMap;
static UIWidget* createFromName( std::string widgetName );

View File

@@ -243,21 +243,16 @@ bool FileInfo::linksToDirectory() const {
std::string FileInfo::linksTo() const {
#if EE_PLATFORM != EE_PLATFORM_WIN
if ( isLink() ) {
char* ch = realpath( mFilepath.c_str(), NULL );
if ( NULL != ch ) {
std::string tstr( ch );
free( ch );
return tstr;
}
}
if ( isLink() )
return getRealPath();
#endif
return std::string( "" );
}
std::string FileInfo::getRealPath() const {
return FileSystem::getRealPath( getFilepath() );
}
bool FileInfo::exists() const {
if ( isDirectory() )
FileSystem::dirRemoveSlashAtEnd( mFilepath );

View File

@@ -7,6 +7,7 @@
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uiscrollbar.hpp>
#include <eepp/window/engine.hpp>
#include <eepp/window/input.hpp>
namespace EE { namespace UI { namespace Abstract {
@@ -421,7 +422,12 @@ UITableRow* UIAbstractTableView::createRow() {
rowWidget->addEventListener( Event::MouseDown, [&]( const Event* event ) {
if ( !( event->asMouseEvent()->getFlags() & EE_BUTTON_LMASK ) || !isRowSelection() )
return;
getSelection().set( event->getNode()->asType<UITableRow>()->getCurIndex() );
auto index = event->getNode()->asType<UITableRow>()->getCurIndex();
if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) {
getSelection().remove( index );
} else {
getSelection().set( index );
}
} );
return rowWidget;
}
@@ -478,7 +484,11 @@ void UIAbstractTableView::bindNavigationClick( UIWidget* widget ) {
onOpenModelIndex( idx, event );
} else if ( isCellSelection() && ( mouseEvent->getFlags() & EE_BUTTON_LMASK ) ) {
auto cellIdx = mouseEvent->getNode()->asType<UITableCell>()->getCurIndex();
getSelection().set( cellIdx );
if ( getUISceneNode()->getWindow()->getInput()->isControlPressed() ) {
getSelection().remove( cellIdx );
} else {
getSelection().set( cellIdx );
}
}
} ) );
}

View File

@@ -3483,12 +3483,30 @@ TableView#locate_bar_table > tableview::row:selected > tableview::cell:nth-child
.theme-none > treeview::cell::text,
.theme-none > listview::cell::text,
.none {
color: #b26818;
color: #d48838;
}
Anchor.success:hover,
Anchor.error:hover {
color: var(--primary);
}
.status_build_output_cont > SelectButton {
font-size: 11dp;
padding: 2dp 8dp 2dp 8dp;
border-radius: 0dp;
border-width: 1dp;
border-color: transparent;
transition: all 0.2;
}
.status_build_output_cont > SelectButton:hover {
border-width: 1dp;
border-color: var(--primary);
}
#build_output_issues TableView::cell.theme-warning > TableView::cell::icon {
tint: var(--theme-warning);
}
#build_output_issues TableView::cell.theme-error > TableView::cell::icon {
tint: var(--theme-error);
}
</style>
<MainLayout id="main_layout" lw="mp" lh="mp">
<Splitter id="project_splitter" lw="mp" lh="mp">
@@ -3559,17 +3577,17 @@ Anchor.error:hover {
</locatebar>
<globalsearchbar id="global_search_bar" lw="mp" lh="wc">
<hbox lw="mp" lh="wc">
<TextView lw="wc" lh="wc" text='@string(search_for, "Search for:")' margin-right="4dp" />
<TextView text='@string(search_for, "Search for:")' margin-right="4dp" />
<vbox lw="0" lw8="1" lh="wc">
<TextInput id="global_search_find" lw="mp" lh="wc" lh="18dp" padding="0" margin-bottom="2dp" />
<hbox lw="mp" lh="wc" margin-bottom="4dp">
<CheckBox id="case_sensitive" lw="wc" lh="wc" text='@string(case_sensitive, "Case sensitive")' selected="true" />
<CheckBox id="whole_word" lw="wc" lh="wc" text='@string(match_whole_word, "Match Whole Word")' selected="false" margin-left="8dp" />
<CheckBox id="lua_pattern" lw="wc" lh="wc" text='@string(lua_pattern, "Lua Pattern")' selected="false" margin-left="8dp" />
<CheckBox id="escape_sequence" lw="wc" lh="wc" text='@string(use_escape_sequences, "Use escape sequences")' margin-left="8dp" selected="false" tooltip='@string(escape_sequence_tooltip, "Replace \\, \t, \n, \r and \uXXXX (Unicode characters) with the corresponding control")' />
<CheckBox id="case_sensitive" text='@string(case_sensitive, "Case sensitive")' selected="true" />
<CheckBox id="whole_word" text='@string(match_whole_word, "Match Whole Word")' selected="false" margin-left="8dp" />
<CheckBox id="lua_pattern" text='@string(lua_pattern, "Lua Pattern")' selected="false" margin-left="8dp" />
<CheckBox id="escape_sequence" text='@string(use_escape_sequences, "Use escape sequences")' margin-left="8dp" selected="false" tooltip='@string(escape_sequence_tooltip, "Replace \\, \t, \n, \r and \uXXXX (Unicode characters) with the corresponding control")' />
</hbox>
<hbox lw="mp" lh="wc">
<TextView lw="wc" lh="wc" text='@string(history, "History:")' margin-right="4dp" lh="18dp" />
<TextView text='@string(history, "History:")' margin-right="4dp" lh="18dp" />
<DropDownList id="global_search_history" lw="0" lh="18dp" lw8="1" margin-right="4dp" />
<PushButton id="global_search_clear_history" lw="wc" lh="18dp" text='@string(clear_history, "Clear History")' margin-right="4dp" />
<PushButton id="global_search" lw="wc" lh="18dp" text='@string(search, "Search")' margin-right="4dp" />
@@ -3583,86 +3601,80 @@ Anchor.error:hover {
<TextView class="status_but" id="status_locate_bar" text="@string(locate, Locate)" />
<TextView class="status_but" id="status_global_search_bar" text="@string(search, Search)" />
<TextView class="status_but" id="status_terminal" text="@string(terminal, Terminal)" />
<TextView class="status_but" id="status_build_output" text="@string(build_output, Build Output)" />
<TextView class="status_but" id="status_build_output" text="@string(build, Build)" />
<View lw="0" lw8="1" lh="mp" />
</statusbar>
</vbox>
</Splitter>
<TextView id="settings" lw="wc" lh="wc" text="&#xf0e9;" lg="top|right" />
<TextView id="settings" text="&#xf0e9;" lg="top|right" />
</MainLayout>
)html";
UIIconTheme* iconTheme = UIIconTheme::New( "ecode" );
mMenuIconSize = mConfig.ui.fontSize.asPixels( 0, Sizef(), mDisplayDPI );
std::unordered_map<std::string, Uint32> icons = {
{ "document-new", 0xecc3 },
{ "document-open", 0xed70 },
{ "document-save", 0xf0b3 },
{ "document-save-as", 0xf0b3 },
{ "document-close", 0xeb99 },
{ "quit", 0xeb97 },
{ "undo", 0xea58 },
{ "redo", 0xea5a },
{ "cut", 0xf0c1 },
{ "copy", 0xecd5 },
{ "paste", 0xeb91 },
{ "edit", 0xec86 },
{ "split-horizontal", 0xf17a },
{ "split-vertical", 0xf17b },
{ "find-replace", 0xed2b },
// { "folder", 0xed54 },
// { "folder-open", 0xed70 },
{ "folder-add", 0xed5a },
// { "file", 0xecc3 },
{ "file-add", 0xecc9 },
{ "file-copy", 0xecd3 },
{ "file-code", 0xecd1 },
{ "file-edit", 0xecdb },
{ "font-size", 0xed8d },
{ "delete-bin", 0xec1e },
{ "delete-text", 0xec1e },
{ "zoom-in", 0xf2db },
{ "zoom-out", 0xf2dd },
{ "zoom-reset", 0xeb47 },
{ "fullscreen", 0xed9c },
{ "keybindings", 0xee75 },
// { "tree-expanded", 0xea50 },
// { "tree-contracted", 0xea54 },
{ "search", 0xf0d1 },
{ "go-up", 0xea78 },
{ "ok", 0xeb7a },
{ "cancel", 0xeb98 },
{ "color-picker", 0xf13d },
{ "pixel-density", 0xed8c },
{ "go-to-line", 0xf1f8 },
{ "table-view", 0xf1de },
{ "list-view", 0xecf1 },
{ "menu-unfold", 0xef40 },
{ "menu-fold", 0xef3d },
{ "download-cloud", 0xec58 },
{ "layout-left", 0xee94 },
{ "layout-right", 0xee9b },
{ "color-scheme", 0xebd4 },
{ "global-settings", 0xedcf },
{ "folder-user", 0xed84 },
{ "help", 0xf045 },
{ "terminal", 0xf1f6 },
{ "earth", 0xec7a },
{ "arrow-down", 0xea4c },
{ "arrow-up", 0xea76 },
{ "arrow-down-s", 0xea4e },
{ "arrow-up-s", 0xea78 },
{ "arrow-right-s", 0xea6e },
{ "match-case", 0xed8d },
{ "palette", 0xefc5 },
{ "file-code", 0xecd1 },
{ "cursor-pointer", 0xec09 },
{ "drive", 0xedf8 },
{ "refresh", 0xf064 },
{ "hearth-pulse", 0xee10 },
{ "add", 0xea12 },
{ "hammer", 0xedee },
{ "eraser", 0xec9e } };
std::unordered_map<std::string, Uint32> icons = { { "document-new", 0xecc3 },
{ "document-open", 0xed70 },
{ "document-save", 0xf0b3 },
{ "document-save-as", 0xf0b3 },
{ "document-close", 0xeb99 },
{ "quit", 0xeb97 },
{ "undo", 0xea58 },
{ "redo", 0xea5a },
{ "cut", 0xf0c1 },
{ "copy", 0xecd5 },
{ "paste", 0xeb91 },
{ "edit", 0xec86 },
{ "split-horizontal", 0xf17a },
{ "split-vertical", 0xf17b },
{ "find-replace", 0xed2b },
{ "folder-add", 0xed5a },
{ "file-add", 0xecc9 },
{ "file-copy", 0xecd3 },
{ "file-code", 0xecd1 },
{ "file-edit", 0xecdb },
{ "font-size", 0xed8d },
{ "delete-bin", 0xec1e },
{ "delete-text", 0xec1e },
{ "zoom-in", 0xf2db },
{ "zoom-out", 0xf2dd },
{ "zoom-reset", 0xeb47 },
{ "fullscreen", 0xed9c },
{ "keybindings", 0xee75 },
{ "search", 0xf0d1 },
{ "go-up", 0xea78 },
{ "ok", 0xeb7a },
{ "cancel", 0xeb98 },
{ "color-picker", 0xf13d },
{ "pixel-density", 0xed8c },
{ "go-to-line", 0xf1f8 },
{ "table-view", 0xf1de },
{ "list-view", 0xecf1 },
{ "menu-unfold", 0xef40 },
{ "menu-fold", 0xef3d },
{ "download-cloud", 0xec58 },
{ "layout-left", 0xee94 },
{ "layout-right", 0xee9b },
{ "color-scheme", 0xebd4 },
{ "global-settings", 0xedcf },
{ "folder-user", 0xed84 },
{ "help", 0xf045 },
{ "terminal", 0xf1f6 },
{ "earth", 0xec7a },
{ "arrow-down", 0xea4c },
{ "arrow-up", 0xea76 },
{ "arrow-down-s", 0xea4e },
{ "arrow-up-s", 0xea78 },
{ "arrow-right-s", 0xea6e },
{ "match-case", 0xed8d },
{ "palette", 0xefc5 },
{ "file-code", 0xecd1 },
{ "cursor-pointer", 0xec09 },
{ "drive", 0xedf8 },
{ "refresh", 0xf064 },
{ "hearth-pulse", 0xee10 },
{ "add", 0xea12 },
{ "hammer", 0xedee },
{ "eraser", 0xec9e } };
for ( const auto& icon : icons )
iconTheme->add( UIGlyphIcon::New( icon.first, iconFont, icon.second ) );
@@ -3783,7 +3795,9 @@ Anchor.error:hover {
{ "chevron-right", 0xeab6 },
{ "lightbulb-autofix", 0xeb13 },
{ "layout-sidebar-left-off", 0xec02 },
{ "layout-sidebar-left", 0xebf3 } };
{ "layout-sidebar-left", 0xebf3 },
{ "warning", 0xea6c },
{ "error", 0xea87 } };
for ( const auto& icon : codIcons )
iconTheme->add( UIGlyphIcon::New( icon.first, codIconFont, icon.second ) );
@@ -3815,6 +3829,7 @@ Anchor.error:hover {
UIWidgetCreator::registerWidget( "globalsearchbar", UIGlobalSearchBar::New );
UIWidgetCreator::registerWidget( "mainlayout", UIMainLayout::New );
UIWidgetCreator::registerWidget( "statusbar", UIStatusBar::New );
UIWidgetCreator::registerWidget( "rellayce", UIRelativeLayoutCommandExecuter::New );
mUISceneNode->loadLayoutFromString( baseUI );
mUISceneNode->bind( "main_layout", mMainLayout );
mUISceneNode->bind( "code_container", mBaseLayout );

View File

@@ -373,15 +373,18 @@ void FeaturesHealth::displayHealth( PluginManager* pluginManager, UISceneNode* s
table->setFitAllColumnsToWidget( true );
table->setModel( model );
auto healthLangInfo = win->find( "health-lang-info" );
table->setOnSelection( [table, healthLangInfo, sceneNode]( const ModelIndex& index ) {
table->setOnSelectionChange( [table, healthLangInfo, sceneNode]() {
if ( table->getSelection().isEmpty() || nullptr == table->getModel() ) {
healthLangInfo->setVisible( false );
return;
}
ModelIndex index = table->getSelection().first();
static const std::string none = sceneNode->i18n( "none", "None" );
static const std::string notFound =
sceneNode->i18n( "not_found_in_path", "Not found in $PATH" );
static const std::string patherr =
String::format( "%s: %s", I18N( "path_is", "PATH is" ), std::getenv( "PATH" ) );
if ( nullptr == table->getModel() )
return;
HealthModel* model = static_cast<HealthModel*>( table->getModel() );
const auto& lang = model->getHealthRow( index.row() );
healthLangInfo->childsCloseAll();

View File

@@ -285,25 +285,25 @@ void LSPDocumentClient::highlight() {
start = deltaStart;
}
auto& line = tokenizerLines[currentLine];
auto* line = &tokenizerLines[currentLine];
if ( type >= 0 && type < (int)caps.legend.tokenTypes.size() ) {
const auto& ltype = caps.legend.tokenTypes[type];
line.tokens.push_back(
line->tokens.push_back(
{ semanticTokenTypeToSyntaxType( ltype, mDoc->getSyntaxDefinition() ), start,
len } );
} else {
line.tokens.push_back( { "normal", start, len } );
line->tokens.push_back( { "normal", start, len } );
}
line.hash = mDoc->line( currentLine ).getHash();
line.updateSignature();
line->hash = mDoc->line( currentLine ).getHash();
line->updateSignature();
auto curSignature = mDoc->getHighlighter()->getTokenizedLineSignature( lastLine );
if ( lastLinePtr && lastLinePtr->signature == curSignature ) {
if ( lastLine != currentLine && lastLinePtr && lastLinePtr->signature == curSignature ) {
tokenizerLines.erase( lastLine );
}
lastLine = currentLine;
lastLinePtr = &line;
lastLinePtr = line;
}
diff = clock.getElapsedTime();

View File

@@ -57,6 +57,7 @@ static void replaceVar( ProjectBuildStep& s, const std::string& var, const std::
String::replaceAll( s.cmd, var, val );
String::replaceAll( s.args, var, val );
String::replaceAll( s.workingDir, slashDup, FileSystem::getOSSlash() );
FileSystem::dirAddSlashAtEnd( s.workingDir );
}
ProjectBuildSteps ProjectBuild::replaceVars( const ProjectBuildSteps& steps ) const {
@@ -686,27 +687,33 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str
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() ) );
100,
Sys::getDateTimeStr() + ": " +
String::format(
i18n( "build_elapsed_time", "Elapsed Time: %s.\n" ).toUtf8().c_str(),
clock.getElapsedTime().toString().c_str() ),
nullptr );
}
};
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() ) );
progressFn( 0,
Sys::getDateTimeStr() + ": " +
String::format(
i18n( "running_steps_for_project", "Running steps for project %s...\n" )
.toUtf8()
.c_str(),
buildName.c_str() ),
nullptr );
if ( !buildType.empty() )
progressFn(
0, Sys::getDateTimeStr() + ": " +
String::format(
i18n( "using_build_type", "Using build type: %s.\n" ).toUtf8().c_str(),
buildType.c_str() ) );
0,
Sys::getDateTimeStr() + ": " +
String::format(
i18n( "using_build_type", "Using build type: %s.\n" ).toUtf8().c_str(),
buildType.c_str() ),
nullptr );
}
int c = 0;
@@ -742,7 +749,8 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str
Sys::getDateTimeStr() + ": " +
String::format(
i18n( "starting_process", "Starting %s %s\n" ).toUtf8().c_str(),
cmd.cmd.c_str(), cmd.args.c_str() ) );
cmd.cmd.c_str(), cmd.args.c_str() ),
nullptr );
std::string buffer( 1024, '\0' );
unsigned bytesRead = 0;
@@ -751,7 +759,7 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str
bytesRead = mProcess->readStdOut( buffer );
std::string data( buffer.substr( 0, bytesRead ) );
if ( progressFn )
progressFn( progress, std::move( data ) );
progressFn( progress, std::move( data ), &cmd );
} while ( bytesRead != 0 && mProcess->isAlive() && !mShuttingDown && !mCancelBuild );
if ( mShuttingDown || mCancelBuild ) {
@@ -759,7 +767,7 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str
mCancelBuild = false;
printElapsed();
if ( doneFn )
doneFn( EXIT_FAILURE );
doneFn( EXIT_FAILURE, &cmd );
return;
}
@@ -773,11 +781,12 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str
"The process \"%s\" exited with errors.\n" )
.toUtf8()
.c_str(),
cmd.cmd.c_str() ) );
cmd.cmd.c_str() ),
nullptr );
}
printElapsed();
if ( doneFn )
doneFn( returnCode );
doneFn( returnCode, &cmd );
return;
} else {
if ( progressFn ) {
@@ -786,22 +795,24 @@ void ProjectBuildManager::runBuild( const std::string& buildName, const std::str
"The process \"%s\" exited normally.\n" )
.toUtf8()
.c_str(),
cmd.cmd.c_str() ) );
cmd.cmd.c_str() ),
nullptr );
}
}
} else {
printElapsed();
if ( doneFn )
doneFn( EXIT_FAILURE );
doneFn( EXIT_FAILURE, nullptr );
return;
}
c++;
if ( c == (int)res.cmds.size() ) {
printElapsed();
if ( doneFn )
doneFn( EXIT_SUCCESS, &cmd );
}
}
printElapsed();
if ( doneFn )
doneFn( EXIT_SUCCESS );
}
void ProjectBuildManager::buildSidePanelTab() {

View File

@@ -95,6 +95,13 @@ struct ProjectBuildConfig {
enum class ProjectOutputParserTypes { Error = 0, Warning = 1, Notice = 2 };
struct PatternOrder {
int file{ 1 };
int line{ 2 };
int col{ 3 };
int message{ 4 };
};
struct ProjectBuildOutputParserConfig {
static std::string typeToString( ProjectOutputParserTypes type ) {
switch ( type ) {
@@ -110,12 +117,7 @@ struct ProjectBuildOutputParserConfig {
ProjectOutputParserTypes type;
std::string pattern;
struct {
int file{ 1 };
int line{ 2 };
int col{ 3 };
int message{ 4 };
} patternOrder;
PatternOrder patternOrder;
};
class ProjectBuildOutputParser {
@@ -223,8 +225,9 @@ struct ProjectBuildCommandsRes {
bool isValid() { return errorMsg.empty(); }
};
using ProjectBuildProgressFn = std::function<void( int curProgress, std::string buffer )>;
using ProjectBuildDoneFn = std::function<void( int exitCode )>;
using ProjectBuildProgressFn =
std::function<void( int curProgress, std::string buffer, const ProjectBuildCommand* cmd )>;
using ProjectBuildDoneFn = std::function<void( int exitCode, const ProjectBuildCommand* cmd )>;
using ProjectBuildi18nFn =
std::function<String( const std::string& /*key*/, const String& /*defaultvalue*/ )>;

View File

@@ -1,5 +1,6 @@
#include "statusbuildoutputcontroller.hpp"
#include "ecode.hpp"
#include "widgetcommandexecuter.hpp"
namespace ecode {
@@ -40,9 +41,7 @@ void StatusBuildOutputController::hide() {
void StatusBuildOutputController::show() {
if ( nullptr == mContainer ) {
mMainSplitter->updateLayout();
mContainer = createContainer();
mContainer->setId( "build_output" );
mContainer->setVisible( false );
createContainer();
}
if ( !mContainer->isVisible() ) {
@@ -55,7 +54,7 @@ void StatusBuildOutputController::show() {
}
mContainer->setParent( mMainSplitter );
mContainer->setVisible( true );
mContainer->setFocus();
mContainer->getFirstChild()->setFocus();
mApp->getStatusBar()->updateState();
}
}
@@ -90,6 +89,53 @@ UIPushButton* StatusBuildOutputController::getCleanButton( App* app ) {
return nullptr;
}
bool StatusBuildOutputController::searchFindAndAddStatusResult(
const std::vector<PatternHolder>& patterns, const std::string& text,
const ProjectBuildCommand* cmd ) {
LuaPattern::Range matches[12];
for ( const auto& pattern : patterns ) {
if ( pattern.pattern.matches( text, matches ) ) {
StatusMessage status;
status.type = pattern.config.type;
for ( int i = 0; i < (int)pattern.pattern.getNumMatches(); ++i ) {
if ( !matches[i].isValid() )
break;
if ( i == 0 ) {
status.output = text;
continue;
}
std::string subtxt = text.substr( matches[i].start, matches[i].end );
if ( pattern.config.patternOrder.message == i ) {
auto nl = subtxt.find_first_of( '\n' );
if ( nl == std::string::npos ) {
status.message = std::move( subtxt );
} else {
status.message = subtxt.substr( 0, nl );
}
} else if ( pattern.config.patternOrder.file == i ) {
status.file = FileSystem::getRealPath( cmd->workingDir + subtxt );
status.fileName = FileSystem::fileNameFromPath( status.file );
} else if ( pattern.config.patternOrder.line == i ) {
int l;
if ( String::fromString( l, subtxt ) )
status.line = l;
} else if ( pattern.config.patternOrder.col == i ) {
int c;
if ( String::fromString( c, subtxt ) )
status.col = c;
}
}
mStatusResults.emplace_back( status );
return true;
}
}
return false;
}
void StatusBuildOutputController::runBuild( const std::string& buildName,
const std::string& buildType,
const ProjectBuildOutputParser& outputParser ) {
@@ -99,17 +145,27 @@ void StatusBuildOutputController::runBuild( const std::string& buildName,
auto pbm = mApp->getProjectBuildManager();
show();
showBuildOutput();
mContainer->getDocument().reset();
mContainer->setScrollY( mContainer->getMaxScroll().y );
mStatusResults.clear();
if ( mTableIssues )
mTableIssues->getSelection().clear();
mBuildOutput->getDocument().reset();
mBuildOutput->setScrollY( mBuildOutput->getMaxScroll().y );
std::vector<SyntaxPattern> patterns;
mPatternHolder.clear();
mCurLineBuffer.clear();
auto configs = { outputParser.getPresetConfig(), outputParser.getConfig() };
for ( const auto& config : configs ) {
for ( const auto& parser : config ) {
mPatternHolder.push_back( { LuaPattern( parser.pattern ), parser } );
SyntaxPattern ptn( { parser.pattern },
getProjectOutputParserTypeToString( parser.type ) );
patterns.emplace_back( std::move( ptn ) );
}
}
@@ -123,9 +179,9 @@ void StatusBuildOutputController::runBuild( const std::string& buildName,
SyntaxDefinition synDef( "custom_build", {}, patterns );
mContainer->getDocument().setSyntaxDefinition( synDef );
mContainer->getVScrollBar()->setValue( 1.f );
mContainer->getDocument().getHighlighter()->setMaxTokenizationLength( 2048 );
mBuildOutput->getDocument().setSyntaxDefinition( synDef );
mBuildOutput->getVScrollBar()->setValue( 1.f );
mBuildOutput->getDocument().getHighlighter()->setMaxTokenizationLength( 2048 );
UIPushButton* buildButton = getBuildButton( mApp );
if ( buildButton )
@@ -140,15 +196,33 @@ void StatusBuildOutputController::runBuild( const std::string& buildName,
auto res = pbm->build(
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 );
[this]( auto, std::string buffer, const ProjectBuildCommand* cmd ) {
mBuildOutput->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mBuildOutput->getVScrollBar()->getValue() == 1.f;
mBuildOutput->getDocument().textInput( buffer );
if ( scrollToBottom )
mContainer->setScrollY( mContainer->getMaxScroll().y );
mBuildOutput->setScrollY( mBuildOutput->getMaxScroll().y );
} );
if ( nullptr == cmd )
return;
do {
auto nl = buffer.find_first_of( '\n' );
if ( nl != std::string::npos ) {
mCurLineBuffer += buffer.substr( 0, nl );
searchFindAndAddStatusResult( mPatternHolder, mCurLineBuffer, cmd );
buffer = buffer.substr( nl + 1 );
mCurLineBuffer.clear();
} else {
mCurLineBuffer += buffer;
buffer.clear();
}
} while ( !buffer.empty() );
},
[this, enableCleanButton]( auto exitCode ) {
[this, enableCleanButton]( auto exitCode, const ProjectBuildCommand* cmd ) {
if ( !mCurLineBuffer.empty() && nullptr != cmd )
searchFindAndAddStatusResult( mPatternHolder, mCurLineBuffer, cmd );
String buffer;
if ( EXIT_SUCCESS == exitCode ) {
@@ -159,11 +233,11 @@ void StatusBuildOutputController::runBuild( const std::string& buildName,
mApp->i18n( "build_failed", "Build run with errors\n" );
}
mContainer->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mContainer->getVScrollBar()->getValue() == 1.f;
mContainer->getDocument().textInput( buffer );
mBuildOutput->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mBuildOutput->getVScrollBar()->getValue() == 1.f;
mBuildOutput->getDocument().textInput( buffer );
if ( scrollToBottom )
mContainer->setScrollY( mContainer->getMaxScroll().y );
mBuildOutput->setScrollY( mBuildOutput->getMaxScroll().y );
} );
UIPushButton* buildButton = getBuildButton( mApp );
@@ -192,8 +266,8 @@ void StatusBuildOutputController::runClean( const std::string& buildName,
show();
mContainer->getDocument().reset();
mContainer->setScrollY( mContainer->getMaxScroll().y );
mBuildOutput->getDocument().reset();
mBuildOutput->setScrollY( mBuildOutput->getMaxScroll().y );
std::vector<SyntaxPattern> patterns;
@@ -211,8 +285,8 @@ void StatusBuildOutputController::runClean( const std::string& buildName,
SyntaxDefinition synDef( "custom_build", {}, patterns );
mContainer->getDocument().setSyntaxDefinition( synDef );
mContainer->getVScrollBar()->setValue( 1.f );
mBuildOutput->getDocument().setSyntaxDefinition( synDef );
mBuildOutput->getVScrollBar()->setValue( 1.f );
UIPushButton* buildButton = getBuildButton( mApp );
bool enableBuildButton = false;
@@ -227,15 +301,15 @@ void StatusBuildOutputController::runClean( const std::string& buildName,
auto res = pbm->clean(
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 );
[this]( auto, auto buffer, auto ) {
mBuildOutput->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mBuildOutput->getVScrollBar()->getValue() == 1.f;
mBuildOutput->getDocument().textInput( buffer );
if ( scrollToBottom )
mContainer->setScrollY( mContainer->getMaxScroll().y );
mBuildOutput->setScrollY( mBuildOutput->getMaxScroll().y );
} );
},
[this, enableBuildButton]( auto exitCode ) {
[this, enableBuildButton]( auto exitCode, auto ) {
String buffer;
if ( EXIT_SUCCESS == exitCode ) {
@@ -246,11 +320,11 @@ void StatusBuildOutputController::runClean( const std::string& buildName,
mApp->i18n( "clean_failed", "Clean run with errors\n" );
}
mContainer->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mContainer->getVScrollBar()->getValue() == 1.f;
mContainer->getDocument().textInput( buffer );
mBuildOutput->runOnMainThread( [this, buffer]() {
bool scrollToBottom = mBuildOutput->getVScrollBar()->getValue() == 1.f;
mBuildOutput->getDocument().textInput( buffer );
if ( scrollToBottom )
mContainer->setScrollY( mContainer->getMaxScroll().y );
mBuildOutput->setScrollY( mBuildOutput->getMaxScroll().y );
} );
UIPushButton* cleanButton = getCleanButton( mApp );
@@ -270,11 +344,130 @@ void StatusBuildOutputController::runClean( const std::string& buildName,
}
UICodeEditor* StatusBuildOutputController::getContainer() {
return mContainer;
return mBuildOutput;
}
UICodeEditor* StatusBuildOutputController::createContainer() {
UICodeEditor* editor = UICodeEditor::NewOpt( true, true );
void StatusBuildOutputController::showIssues() {
mBuildOutput->setVisible( false );
mTableIssues->setVisible( true );
mButOutput->setSelected( false );
mButIssues->setSelected( true );
mTableIssues->setFocus();
}
void StatusBuildOutputController::showBuildOutput() {
mBuildOutput->setVisible( true );
mTableIssues->setVisible( false );
mButOutput->setSelected( true );
mButIssues->setSelected( false );
mBuildOutput->setFocus();
}
class StatusMessageModel : public Model {
public:
static std::shared_ptr<StatusMessageModel> create( std::vector<StatusMessage>& data,
UISceneNode* sceneNode ) {
return std::make_shared<StatusMessageModel>( data, sceneNode );
}
explicit StatusMessageModel( std::vector<StatusMessage>& data, UISceneNode* sceneNode ) :
mData( data ), mSceneNode( sceneNode ) {}
virtual size_t rowCount( const ModelIndex& ) const { return mData.size(); }
virtual size_t columnCount( const ModelIndex& ) const { return 3; }
virtual Variant data( const ModelIndex& index, ModelRole role ) const {
eeASSERT( index.row() < (Int64)mData.size() );
static UIIcon* errorIcon = mSceneNode->findIcon( "error" );
static UIIcon* warnIcon = mSceneNode->findIcon( "warning" );
if ( role == ModelRole::Display ) {
switch ( index.column() ) {
case 2:
return Variant( mData[index.row()].line );
case 1:
return Variant( mData[index.row()].fileName.c_str() );
case 0:
default:
return Variant( mData[index.row()].message );
}
} else if ( role == ModelRole::Icon && index.column() == 0 ) {
return Variant( mData[index.row()].type == ProjectOutputParserTypes::Error ? errorIcon
: warnIcon );
} else if ( role == ModelRole::Class ) {
return Variant( mData[index.row()].type == ProjectOutputParserTypes::Error
? "theme-error"
: "theme-warning" );
} else if ( role == ModelRole::Custom ) {
switch ( index.column() ) {
case 0:
return Variant( mData[index.row()].file.c_str() );
case 1:
return Variant( mData[index.row()].line );
case 2:
return Variant( mData[index.row()].col );
}
}
return {};
}
virtual void update() { onModelUpdate(); }
virtual std::string columnName( const size_t& idx ) const {
switch ( idx ) {
case 2:
return mSceneNode->i18n( "message", "Message" );
case 1:
return mSceneNode->i18n( "file", "File" );
case 0:
return mSceneNode->i18n( "line", "Line" );
}
return "";
}
virtual bool classModelRoleEnabled() { return true; }
protected:
std::vector<StatusMessage>& mData;
UISceneNode* mSceneNode;
};
void StatusBuildOutputController::onLoadDone( const Variant& lineNum, const Variant& colNum ) {
if ( mSplitter->curEditorExistsAndFocused() && lineNum.isValid() && colNum.isValid() &&
lineNum.is( Variant::Type::Int64 ) && colNum.is( Variant::Type::Int64 ) ) {
TextPosition pos{ lineNum.asInt64() > 0 ? lineNum.asInt64() - 1 : 0, colNum.asInt64() };
mSplitter->getCurEditor()->getDocument().setSelection( pos );
mSplitter->getCurEditor()->goToLine( pos );
}
}
void StatusBuildOutputController::setHeaderWidth() {
auto totWidth = mTableIssues->getPixelsSize().getWidth() -
( mTableIssues->getVerticalScrollBar()->isVisible()
? mTableIssues->getVerticalScrollBar()->getPixelsSize().getWidth()
: 0.f );
mTableIssues->setColumnWidth( 0, totWidth * 0.80f );
mTableIssues->setColumnWidth( 1, totWidth * 0.15f );
mTableIssues->setColumnWidth( 2, totWidth * 0.05f );
}
void StatusBuildOutputController::createContainer() {
if ( mContainer )
return;
const auto XML = R"xml(
<RelativeLayout id="build_output" lw="mp" lh="mp" visible="false">
<rellayce class="status_build_output_cont" lw="mp" lh="mp">
<CodeEditor id="build_output_output" lw="mp" lh="mp" />
<TableView id="build_output_issues" lw="mp" lh="mp" visible="false" />
<SelectButton id="but_build_output_issues" text="@string(issues, Issues)" lg="bottom|right" margin-right="1dp" margin-bottom="18dp" margin-right="18dp" />
<SelectButton id="but_build_output_output" text="@string(output, Output)" layout-to-left-of="but_build_output_issues" selected="true" />
</rellayce>
</RelativeLayout>
)xml";
mContainer = mApp->getUISceneNode()
->loadLayoutFromString( XML, mMainSplitter )
->asType<UIRelativeLayoutCommandExecuter>();
auto editor = mContainer->find<UICodeEditor>( "build_output_output" );
editor->setLocked( true );
editor->setLineBreakingColumn( 0 );
editor->setShowLineNumber( false );
@@ -282,7 +475,57 @@ UICodeEditor* StatusBuildOutputController::createContainer() {
editor->getDocument().textInput(
mApp->i18n( "no_build_has_been_run", "No build has been run" ) );
editor->setScrollY( editor->getMaxScroll().y );
return editor;
mButOutput = mContainer->find<UISelectButton>( "but_build_output_output" );
mButIssues = mContainer->find<UISelectButton>( "but_build_output_issues" );
mTableIssues = mContainer->find<UITableView>( "build_output_issues" );
mTableIssues->setHeadersVisible( true );
mTableIssues->setModel( StatusMessageModel::create( mStatusResults, mApp->getUISceneNode() ) );
setHeaderWidth();
mTableIssues->on( Event::OnSizeChange, [this]( auto ) { setHeaderWidth(); } );
mTableIssues->on( Event::OnModelEvent, [this]( const Event* event ) {
auto modelEvent = static_cast<const ModelEvent*>( event );
auto idx = modelEvent->getModelIndex();
if ( modelEvent->getModel() && modelEvent->getModelEventType() == ModelEventType::Open &&
idx.isValid() ) {
auto model = modelEvent->getModel();
Variant vPath( model->data( idx, ModelRole::Custom ) );
if ( vPath.isValid() && vPath.is( Variant::Type::cstr ) ) {
std::string path( vPath.asCStr() );
UITab* tab = mSplitter->isDocumentOpen( path );
Variant lineNum( model->data( model->index( modelEvent->getModelIndex().row(), 1 ),
ModelRole::Custom ) );
Variant colNum( model->data( model->index( modelEvent->getModelIndex().row(), 2 ),
ModelRole::Custom ) );
if ( !tab ) {
FileInfo fileInfo( path );
if ( fileInfo.exists() && fileInfo.isRegularFile() )
mApp->loadFileFromPath(
path, true, nullptr,
[&, lineNum, colNum]( UICodeEditor*, const std::string& ) {
onLoadDone( lineNum, colNum );
} );
} else {
tab->getTabWidget()->setTabSelected( tab );
onLoadDone( lineNum, colNum );
}
}
}
} );
mBuildOutput = editor;
mContainer->setVisible( false );
auto cont = mContainer->getFirstChild()->asType<UIRelativeLayoutCommandExecuter>();
cont->setCommand( "build-output-show-build-output", [this]() { showBuildOutput(); } );
cont->setCommand( "build-output-show-build-issues", [this]() { showIssues(); } );
cont->getKeyBindings().addKeybind( { KEY_1, KeyMod::getDefaultModifier() },
"build-output-show-build-output" );
cont->getKeyBindings().addKeybind( { KEY_2, KeyMod::getDefaultModifier() },
"build-output-show-build-issues" );
mButOutput->onClick( [this]( auto ) { showBuildOutput(); } );
mButIssues->onClick( [this]( auto ) { showIssues(); } );
mButOutput->setTooltipText(
cont->getKeyBindings().getCommandKeybindString( "build-output-show-build-output" ) );
mButIssues->setTooltipText(
cont->getKeyBindings().getCommandKeybindString( "build-output-show-build-issues" ) );
}
} // namespace ecode

View File

@@ -2,10 +2,13 @@
#define ECODE_STATUSBUILDOUTPUTCONTROLLER_HPP
#include "projectbuild.hpp"
#include <eepp/system/luapattern.hpp>
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include <eepp/ui/uirelativelayout.hpp>
#include <eepp/ui/uiscenenode.hpp>
#include <eepp/ui/uisplitter.hpp>
#include <eepp/ui/uitableview.hpp>
using namespace EE;
using namespace EE::UI;
@@ -15,6 +18,21 @@ namespace ecode {
class App;
struct StatusMessage {
ProjectOutputParserTypes type;
String output;
String message;
std::string file;
std::string fileName;
Int64 line{ 0 };
Int64 col{ 0 };
};
struct PatternHolder {
LuaPattern pattern;
ProjectBuildOutputParserConfig config;
};
class StatusBuildOutputController {
public:
StatusBuildOutputController( UISplitter* mainSplitter, UISceneNode* uiSceneNode, App* app );
@@ -33,18 +51,38 @@ class StatusBuildOutputController {
UICodeEditor* getContainer();
void showIssues();
void showBuildOutput();
protected:
UISplitter* mMainSplitter{ nullptr };
UISceneNode* mUISceneNode{ nullptr };
App* mApp{ nullptr };
UICodeEditorSplitter* mSplitter{ nullptr };
UICodeEditor* mContainer{ nullptr };
UICodeEditor* createContainer();
UIRelativeLayout* mContainer{ nullptr };
UICodeEditor* mBuildOutput{ nullptr };
UISelectButton* mButOutput{ nullptr };
UISelectButton* mButIssues{ nullptr };
UITableView* mTableIssues{ nullptr };
std::vector<StatusMessage> mStatusResults;
std::vector<PatternHolder> mPatternHolder;
std::string mCurLineBuffer;
void createContainer();
UIPushButton* getBuildButton( App* app );
UIPushButton* getCleanButton( App* app );
bool searchFindAndAddStatusResult( const std::vector<PatternHolder>& patterns,
const std::string& text, const ProjectBuildCommand* cmd );
void onLoadDone( const Variant& lineNum, const Variant& colNum );
void setHeaderWidth();
};
} // namespace ecode

View File

@@ -381,7 +381,7 @@ static const auto SETTINGS_PANEL_XML = R"xml(
<vbox lw="mp" lh="wc" class="output_parser">
<TextView class="subtitle" text="@string(output_parser, Output Parser)" />
<TextView lw="mp" lh="wc" word-wrap="true" text="@string(output_parser_desc, Custom output parsers scan command line output for user-provided error patterns to create entries in Issues and highlight those errors on the Build Output)" />
<TextView lw="mp" lh="wc" word-wrap="true" text="@string(output_parser_desc, Custom output parsers scan command line output for user-provided error patterns to create entries in Build Issues and highlight those errors on the Build Output)" />
<TextView lw="mp" lh="wc" word-wrap="true" text='@string(output_parser_preset, "Presets are provided as generic output parsers, you can select one below, by default a \"generic\" preset will be selected:")' />
<DropDownList id="output_parsers_presets_list" layout_width="200dp" layout_height="wc">
<item></item>

View File

@@ -44,7 +44,7 @@ class UIGlobalSearchBar : public UILinearLayout, public WidgetCommandExecuter {
};
class UIMainLayout : public UIRelativeLayout, public WidgetCommandExecuter {
public:
public:
static UIMainLayout* New() { return eeNew( UIMainLayout, () ); }
UIMainLayout() :
@@ -56,6 +56,21 @@ class UIMainLayout : public UIRelativeLayout, public WidgetCommandExecuter {
}
};
class UIRelativeLayoutCommandExecuter : public UIRelativeLayout, public WidgetCommandExecuter {
public:
static UIRelativeLayoutCommandExecuter* New() {
return eeNew( UIRelativeLayoutCommandExecuter, () );
}
UIRelativeLayoutCommandExecuter() :
UIRelativeLayout( "rellayce" ),
WidgetCommandExecuter( getUISceneNode()->getWindow()->getInput() ) {}
virtual Uint32 onKeyDown( const KeyEvent& event ) {
return WidgetCommandExecuter::onKeyDown( event );
}
};
} // namespace ecode
#endif // ECODE_WIDGETCOMMANDEXECUTER_HPP