Plugin preferences configuration WIP.

This commit is contained in:
Martín Lucas Golini
2022-09-28 02:17:33 -03:00
parent 40f005b256
commit 3d3d9057e0
7 changed files with 173 additions and 75 deletions
+16
View File
@@ -41,6 +41,8 @@
--term-back-color: #1e2127;
--term-font-color: #abb2bf;
--highlight-primary: #FFFFFF7A;
--disabled-color: #727679;
--disabled-border: #43474c;
droppable-hovering-color: #FFFFFF20;
}
@@ -168,6 +170,12 @@ SelectButton:selectedpressed {
background-color: var(--primary);
}
pushbutton:disabled,
selectbutton:disabled {
color: var(--disabled-color);
border-color: var(--disabled-border);
}
PushButton::icon,
SelectButton::icon,
TableView::cell::icon,
@@ -1030,6 +1038,8 @@ console {
--floating-icon: #cbcdcd;
--term-back-color: #eff0f1;
--term-font-color: #232627;
--disabled-color: #727679;
--disabled-border: #d3d4d5;
droppable-hovering-color: #00000020;
}
@@ -1061,6 +1071,9 @@ listview::row:selected listview::cell::icon,
tableview::row:selected tableview::cell::expander,
treeview::row:selected treeview::cell::expander,
listview::row:selected listview::cell::expander,
tableview::row:selected tableview::cell checkbox,
treeview::row:selected treeview::cell checkbox,
listview::row:selected listview::cell checkbox,
tableview::row:hover tableview::cell::text,
treeview::row:hover treeview::cell::text,
listview::row:hover listview::cell::text,
@@ -1070,6 +1083,9 @@ listview::row:hover listview::cell::icon,
tableview::row:hover tableview::cell::expander,
treeview::row:hover treeview::cell::expander,
listview::row:hover listview::cell::expander,
tableview::row:hover tableview::cell checkbox,
treeview::row:hover treeview::cell checkbox,
listview::row:hover listview::cell checkbox,
tableview::header::column:hover,
treeview::header::column:hover,
listview::header::column:hover {
+5 -6
View File
@@ -93,17 +93,16 @@ void EventDispatcher::update( const Time& time ) {
mNodeDragging->onCalculateDrag( mMousePos, mInput->getPressTrigger() );
if ( mInput->getPressTrigger() ) {
if ( NULL != mOverNode ) {
mOverNode->onMouseDown( mMousePosi, mInput->getPressTrigger() );
sendMsg( mOverNode, NodeMessage::MouseDown, mInput->getPressTrigger() );
}
if ( !mFirstPress ) {
mDownNode = mOverNode;
mMouseDownPos = mMousePosi;
mFirstPress = true;
}
if ( NULL != mOverNode ) {
mOverNode->onMouseDown( mMousePosi, mInput->getPressTrigger() );
sendMsg( mOverNode, NodeMessage::MouseDown, mInput->getPressTrigger() );
}
}
if ( mInput->getReleaseTrigger() ) {
+8 -1
View File
@@ -1787,7 +1787,14 @@ void App::updateTerminalMenu() {
}
void App::createPluginManagerUI() {
UIPluginManager::New( mUISceneNode, mPluginManager.get() )->showWhenReady();
UIPluginManager::New( mUISceneNode, mPluginManager.get(), [&]( const std::string& path ) {
UITab* tab = mSplitter->isDocumentOpen( path );
if ( !tab ) {
loadFileFromPath( path );
} else {
tab->getTabWidget()->setTabSelected( tab );
}
} )->showWhenReady();
}
void App::updateDocumentMenu() {
+104 -62
View File
@@ -50,83 +50,125 @@ LinterPlugin::~LinterPlugin() {
}
}
void LinterPlugin::load( const PluginManager* pluginManager ) {
std::string path( pluginManager->getResourcesPath() + "plugins/linters.json" );
if ( FileSystem::fileExists( pluginManager->getPluginsPath() + "linters.json" ) )
path = pluginManager->getPluginsPath() + "linters.json";
if ( !FileSystem::fileExists( path ) )
return;
try {
std::ifstream stream( path );
json j;
stream >> j;
bool LinterPlugin::hasFileConfig() {
return !mConfigPath.empty();
}
for ( auto& obj : j ) {
Linter linter;
auto fp = obj["file_patterns"];
std::string LinterPlugin::getFileConfigPath() {
return mConfigPath;
}
for ( auto& pattern : fp )
linter.files.push_back( pattern.get<std::string>() );
auto wp = obj["warning_pattern"];
if ( wp.is_array() ) {
for ( auto& warningPattern : wp )
linter.warningPattern.push_back( warningPattern.get<std::string>() );
} else {
linter.warningPattern = { wp.get<std::string>() };
}
linter.command = obj["command"].get<std::string>();
if ( obj.contains( "expected_exitcodes" ) ) {
auto ee = obj["expected_exitcodes"];
if ( ee.is_array() ) {
for ( auto& number : ee )
if ( number.is_number() )
linter.expectedExitCodes.push_back( number.get<Int64>() );
} else if ( ee.is_number() ) {
linter.expectedExitCodes.push_back( ee.get<Int64>() );
size_t LinterPlugin::linterFilePatternPosition( const std::vector<std::string>& patterns ) {
for ( size_t i = 0; i < mLinters.size(); ++i ) {
for ( const std::string& filePattern : mLinters[i].files ) {
for ( const std::string& pattern : patterns ) {
if ( filePattern == pattern ) {
return i;
}
}
}
}
return std::string::npos;
}
if ( obj.contains( "warning_pattern_order" ) ) {
auto& wpo = obj["warning_pattern_order"];
if ( wpo.contains( "line" ) && wpo["line"].is_number() )
linter.warningPatternOrder.line = wpo["line"].get<int>();
if ( wpo.contains( "col" ) && wpo["col"].is_number() )
linter.warningPatternOrder.col = wpo["col"].get<int>();
if ( wpo.contains( "message" ) && wpo["message"].is_number() )
linter.warningPatternOrder.message = wpo["message"].get<int>();
if ( wpo.contains( "type" ) && wpo["type"].is_number() )
linter.warningPatternOrder.type = wpo["type"].get<int>();
}
void LinterPlugin::loadLinterConfig( const std::string& path ) {
std::ifstream stream( path );
json j;
stream >> j;
if ( obj.contains( "column_starts_at_zero" ) )
linter.columnsStartAtZero = obj["column_starts_at_zero"].get<bool>();
for ( auto& obj : j ) {
Linter linter;
if ( !obj.contains( "file_patterns" ) || !obj.contains( "warning_pattern" ) ||
!obj.contains( "command" ) )
continue;
if ( obj.contains( "deduplicate" ) )
linter.deduplicate = obj["deduplicate"].get<bool>();
auto fp = obj["file_patterns"];
if ( obj.contains( "use_tmp_folder" ) )
linter.useTmpFolder = obj["use_tmp_folder"].get<bool>();
for ( auto& pattern : fp )
linter.files.push_back( pattern.get<std::string>() );
if ( obj.contains( "no_errors_exit_code" ) &&
obj["no_errors_exit_code"].is_number_integer() ) {
linter.hasNoErrorsExitCode = true;
linter.noErrorsExitCode = obj["no_errors_exit_code"].get<int>();
}
auto wp = obj["warning_pattern"];
mLinters.emplace_back( std::move( linter ) );
if ( wp.is_array() ) {
for ( auto& warningPattern : wp )
linter.warningPattern.push_back( warningPattern.get<std::string>() );
} else {
linter.warningPattern = { wp.get<std::string>() };
}
mReady = true;
} catch ( json::exception& e ) {
mReady = false;
Log::error( "Parsing linter failed:\n%s", e.what() );
linter.command = obj["command"].get<std::string>();
if ( obj.contains( "expected_exitcodes" ) ) {
auto ee = obj["expected_exitcodes"];
if ( ee.is_array() ) {
for ( auto& number : ee )
if ( number.is_number() )
linter.expectedExitCodes.push_back( number.get<Int64>() );
} else if ( ee.is_number() ) {
linter.expectedExitCodes.push_back( ee.get<Int64>() );
}
}
if ( obj.contains( "warning_pattern_order" ) ) {
auto& wpo = obj["warning_pattern_order"];
if ( wpo.contains( "line" ) && wpo["line"].is_number() )
linter.warningPatternOrder.line = wpo["line"].get<int>();
if ( wpo.contains( "col" ) && wpo["col"].is_number() )
linter.warningPatternOrder.col = wpo["col"].get<int>();
if ( wpo.contains( "message" ) && wpo["message"].is_number() )
linter.warningPatternOrder.message = wpo["message"].get<int>();
if ( wpo.contains( "type" ) && wpo["type"].is_number() )
linter.warningPatternOrder.type = wpo["type"].get<int>();
}
if ( obj.contains( "column_starts_at_zero" ) )
linter.columnsStartAtZero = obj["column_starts_at_zero"].get<bool>();
if ( obj.contains( "deduplicate" ) )
linter.deduplicate = obj["deduplicate"].get<bool>();
if ( obj.contains( "use_tmp_folder" ) )
linter.useTmpFolder = obj["use_tmp_folder"].get<bool>();
if ( obj.contains( "no_errors_exit_code" ) &&
obj["no_errors_exit_code"].is_number_integer() ) {
linter.hasNoErrorsExitCode = true;
linter.noErrorsExitCode = obj["no_errors_exit_code"].get<int>();
}
// If the file pattern is repeated, we will overwrite the previous linter.
// The previous linter should be the "default" linter that comes with ecode.
size_t pos = linterFilePatternPosition( linter.files );
if ( pos != std::string::npos ) {
mLinters[pos] = linter;
} else {
mLinters.emplace_back( std::move( linter ) );
}
}
}
void LinterPlugin::load( const PluginManager* pluginManager ) {
std::vector<std::string> paths;
std::string path( pluginManager->getResourcesPath() + "plugins/linters.json" );
if ( FileSystem::fileExists( path ) )
paths.emplace_back( path );
path = pluginManager->getPluginsPath() + "linters.json";
if ( FileSystem::fileExists( path ) || FileSystem::fileWrite( path, "[]\n" ) ) {
mConfigPath = path;
paths.emplace_back( path );
}
if ( paths.empty() )
return;
for ( const auto& path : paths ) {
try {
loadLinterConfig( path );
} catch ( json::exception& e ) {
Log::error( "Parsing linter \"%s\" failed:\n%s", path.c_str(), e.what() );
}
}
mReady = !mLinters.empty();
}
void LinterPlugin::onRegister( UICodeEditor* editor ) {
Lock l( mDocMutex );
@@ -60,6 +60,10 @@ class LinterPlugin : public UICodeEditorPlugin {
std::string getDescription() { return Definition().description; }
bool hasFileConfig();
std::string getFileConfigPath();
void onRegister( UICodeEditor* );
void onUnregister( UICodeEditor* );
@@ -94,6 +98,7 @@ class LinterPlugin : public UICodeEditorPlugin {
std::mutex mWorkMutex;
std::condition_variable mWorkerCondition;
Int32 mWorkersCount{ 0 };
std::string mConfigPath;
bool mReady{ false };
bool mShuttingDown{ false };
@@ -116,6 +121,10 @@ class LinterPlugin : public UICodeEditorPlugin {
void invalidateEditors( TextDocument* doc );
std::string getMatchString( const LinterType& type );
void loadLinterConfig( const std::string& path );
size_t linterFilePatternPosition( const std::vector<std::string>& patterns );
};
} // namespace ecode
+28 -4
View File
@@ -1,4 +1,5 @@
#include "pluginmanager.hpp"
#include <eepp/system/filesystem.hpp>
#include <eepp/ui/uicheckbox.hpp>
#include <eepp/ui/uitableview.hpp>
#include <eepp/ui/uiwidgetcreator.hpp>
@@ -171,7 +172,8 @@ class UIPluginManagerTable : public UITableView {
}
};
UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager ) {
UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager,
std::function<void( const std::string& )> loadFileCb ) {
if ( !UIWidgetCreator::isWidgetRegistered( "UIPluginManagerTable" ) )
UIWidgetCreator::registerWidget( "UIPluginManagerTable",
[] { return eeNew( UIPluginManagerTable, () ); } );
@@ -189,7 +191,7 @@ UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager )
<UIPluginManagerTable id="plugin-manager-table" lw="mp" lh="fixed" layout_weight="1" />
<vbox lw="mp" lh="wc">
<hbox margin-top="4dp" layout-gravity="right">
<!-- <pushbutton id="plugin-manager-preferences" enabled="false" text="Enabled" /> -->
<pushbutton id="plugin-manager-preferences" enabled="false" text="Configuration" />
<pushbutton id="plugin-manager-close" text="Close" icon="close" margin-left="4dp" />
</hbox>
</vbox>
@@ -199,18 +201,40 @@ UIWindow* UIPluginManager::New( UISceneNode* sceneNode, PluginManager* manager )
->asType<UIWindow>();
UIWidget* cont = win->getContainer();
UIPushButton* close = cont->find<UIPushButton>( "plugin-manager-close" );
UIPushButton* prefs = cont->find<UIPushButton>( "plugin-manager-preferences" );
UIPluginManagerTable* tv =
win->getContainer()->find<UIPluginManagerTable>( "plugin-manager-table" );
close->addEventListener( Event::MouseClick, [win]( const Event* event ) {
const MouseEvent* mevent = static_cast<const MouseEvent*>( event );
if ( mevent->getFlags() & EE_BUTTON_LMASK )
if ( event->asMouseEvent()->getFlags() & EE_BUTTON_LMASK )
win->closeWindow();
} );
prefs->setText( sceneNode->i18n( "preferences", "Preferences" ) );
win->setTitle( sceneNode->i18n( "plugin_manager", "Plugin Manager" ) );
tv->setModel( PluginsModel::New( manager ) );
tv->setColumnsVisible(
{ PluginsModel::Title, PluginsModel::Description, PluginsModel::Version } );
tv->setAutoColumnsWidth( true );
prefs->addEventListener( Event::MouseClick, [tv, manager, loadFileCb]( const Event* event ) {
if ( event->asMouseEvent()->getFlags() & EE_BUTTON_LMASK &&
!tv->getSelection().isEmpty() ) {
const PluginDefinition* def =
manager->getDefinitionIndex( tv->getSelection().first().row() );
if ( def == nullptr || !manager->isEnabled( def->id ) )
return;
auto* plugin = manager->get( def->id );
if ( !plugin->hasFileConfig() )
return;
if ( FileSystem::fileExists( plugin->getFileConfigPath() ) )
loadFileCb( plugin->getFileConfigPath() );
}
} );
tv->setOnSelection( [&, prefs, manager]( const ModelIndex& index ) {
const PluginDefinition* def = manager->getDefinitionIndex( index.row() );
if ( def == nullptr )
return;
prefs->setEnabled( manager->isEnabled( def->id ) &&
manager->get( def->id )->hasFileConfig() );
} );
win->center();
return win;
}
+3 -2
View File
@@ -99,14 +99,15 @@ class PluginsModel : public Model {
PluginManager* getManager() const;
protected:
protected:
PluginManager* mManager;
std::vector<std::string> mColumnNames{ "Id", "Title", "Enabled", "Description", "Version" };
};
class UIPluginManager {
public:
static UIWindow* New( UISceneNode* sceneNode, PluginManager* manager );
static UIWindow* New( UISceneNode* sceneNode, PluginManager* manager,
std::function<void( const std::string& )> loadFileCb );
};
} // namespace ecode