Fluid: Add automated MergeBack

This commit is contained in:
Matthias Melcher 2026-01-04 20:43:26 +01:00
parent 811a188bbf
commit 7306b66d99
7 changed files with 694 additions and 375 deletions

View File

@ -234,6 +234,7 @@ int Application::run(int argc,char **argv) {
} }
#endif #endif
start_auto_mergeback();
Fl::run(); Fl::run();
proj.undo.clear(); proj.undo.clear();
@ -507,7 +508,9 @@ bool Application::open_project_file(const std::string &filename_arg) {
// clear the project and merge a file by the given name // clear the project and merge a file by the given name
new_project(false); new_project(false);
return merge_project_file(new_filename); bool success = merge_project_file(new_filename);
if (success) mergeback_on_load();
return success;
} }

View File

@ -1226,8 +1226,6 @@ DeclBlock_Node::DeclBlock_Node()
Destructor. Destructor.
*/ */
DeclBlock_Node::~DeclBlock_Node() { DeclBlock_Node::~DeclBlock_Node() {
if (after)
::free((void*)after);
} }
/** /**
@ -1253,7 +1251,7 @@ Node *DeclBlock_Node::make(Strategy strategy) {
DeclBlock_Node *o = new DeclBlock_Node(); DeclBlock_Node *o = new DeclBlock_Node();
o->name("#if 1"); o->name("#if 1");
o->write_map_ = CODE_IN_SOURCE; o->write_map_ = CODE_IN_SOURCE;
o->after = fl_strdup("#endif"); o->end_code_ = "#endif";
o->add(anchor, strategy); o->add(anchor, strategy);
o->factory = this; o->factory = this;
return o; return o;
@ -1272,7 +1270,7 @@ void DeclBlock_Node::write_properties(fld::io::Project_Writer &f) {
if (write_map_ != CODE_IN_SOURCE) if (write_map_ != CODE_IN_SOURCE)
f.write_string("map %d", write_map_); f.write_string("map %d", write_map_);
f.write_string("after"); f.write_string("after");
f.write_word(after); f.write_word(end_code().c_str());
} }
/** /**
@ -1286,7 +1284,7 @@ void DeclBlock_Node::read_property(fld::io::Project_Reader &f, const char *c) {
} else if(!strcmp(c,"map")) { } else if(!strcmp(c,"map")) {
write_map_ = (int)atol(f.read_word()); write_map_ = (int)atol(f.read_word());
} else if (!strcmp(c,"after")) { } else if (!strcmp(c,"after")) {
storestring(f.read_word(),after); end_code(f.read_word());
} else { } else {
Node::read_property(f, c); Node::read_property(f, c);
} }
@ -1317,12 +1315,11 @@ void DeclBlock_Node::write_static(fld::io::Code_Writer& f) {
Write the \b after static code to the source file, and to the header file if declared public. Write the \b after static code to the source file, and to the header file if declared public.
*/ */
void DeclBlock_Node::write_static_after(fld::io::Code_Writer& f) { void DeclBlock_Node::write_static_after(fld::io::Code_Writer& f) {
const char* c = after; if (!end_code().empty()) {
if (c && *c) {
if (write_map_ & STATIC_IN_HEADER) if (write_map_ & STATIC_IN_HEADER)
f.write_h("%s\n", c); f.write_h("%s\n", end_code().c_str());
if (write_map_ & STATIC_IN_SOURCE) if (write_map_ & STATIC_IN_SOURCE)
f.write_c("%s\n", c); f.write_c("%s\n", end_code().c_str());
} }
} }
@ -1344,12 +1341,11 @@ void DeclBlock_Node::write_code1(fld::io::Code_Writer& f) {
Write the \b after code to the source file, and to the header file if declared public. Write the \b after code to the source file, and to the header file if declared public.
*/ */
void DeclBlock_Node::write_code2(fld::io::Code_Writer& f) { void DeclBlock_Node::write_code2(fld::io::Code_Writer& f) {
const char* c = after; if (!end_code().empty()) {
if (c && *c) {
if (write_map_ & CODE_IN_HEADER) if (write_map_ & CODE_IN_HEADER)
f.write_h("%s\n", c); f.write_h("%s\n", end_code().c_str());
if (write_map_ & CODE_IN_SOURCE) if (write_map_ & CODE_IN_SOURCE)
f.write_c("%s\n", c); f.write_c("%s\n", end_code().c_str());
} }
} }

View File

@ -208,8 +208,9 @@ public:
STATIC_IN_HEADER = 4, STATIC_IN_HEADER = 4,
STATIC_IN_SOURCE = 8 STATIC_IN_SOURCE = 8
}; };
private: private:
const char* after { nullptr }; ///< code after all children of this block std::string end_code_; ///< code after all children of this block
int write_map_ { CODE_IN_SOURCE }; ///< see enum above int write_map_ { CODE_IN_SOURCE }; ///< see enum above
public: public:
@ -229,8 +230,8 @@ public:
int is_public() const override; int is_public() const override;
Type type() const override { return Type::DeclBlock; } Type type() const override { return Type::DeclBlock; }
bool is_a(Type inType) const override { return (inType==Type::DeclBlock) ? true : super::is_a(inType); } bool is_a(Type inType) const override { return (inType==Type::DeclBlock) ? true : super::is_a(inType); }
const char *end_code() { return after; } std::string end_code() { return end_code_; }
void end_code(const char *c) { storestring(c, after); } void end_code(const std::string& p) { storestring(p, end_code_); }
int write_map() { return write_map_; } int write_map() { return write_map_; }
void write_map(int v) { write_map_ = v; } void write_map(int v) { write_map_ = v; }
}; };
@ -242,6 +243,7 @@ class Comment_Node : public Node
public: public:
typedef Node super; typedef Node super;
static Comment_Node prototype; static Comment_Node prototype;
private: private:
char in_c_, in_h_, style_; char in_c_, in_h_, style_;
@ -270,17 +272,18 @@ class Class_Node : public Node
public: public:
typedef Node super; typedef Node super;
static Class_Node prototype; static Class_Node prototype;
private: private:
std::string base_class_; std::string base_class_;
std::string prefix_; std::string prefix_;
char public_; char public_;
public: public:
Class_Node(); Class_Node();
~Class_Node(); ~Class_Node();
// state variables for output: // state variables for output:
char write_public_state; // true when public: has been printed char write_public_state; // true when public: has been printed
Class_Node* parent_class; // save class if nested Class_Node* parent_class; // save class if nested
//
Node *make(Strategy strategy) override; Node *make(Strategy strategy) override;
void write_code1(fld::io::Code_Writer& f) override; void write_code1(fld::io::Code_Writer& f) override;
void write_code2(fld::io::Code_Writer& f) override; void write_code2(fld::io::Code_Writer& f) override;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -617,14 +617,24 @@ int Mergeback::merge_back(const std::string &s, const std::string &p, Task task)
0 if MergeBack is not enabled, 0 if MergeBack is not enabled,
or the result of the merge_back function. or the result of the merge_back function.
*/ */
int mergeback_code_files(Project &proj) int mergeback_code_files(Project &proj, Mergeback::Feedback feedback)
{ {
static bool recursion_lock = false;
if (recursion_lock) return;
recursion_lock = true;
Fluid.flush_text_widgets(); Fluid.flush_text_widgets();
if (!proj.proj_filename) return 1; if (!proj.proj_filename) {
recursion_lock = false;
return 1;
}
if (!proj.write_mergeback_data) { if (!proj.write_mergeback_data) {
if (feedback & Mergeback::CHATTY) {
fl_message("MergeBack is not enabled for this project.\n" fl_message("MergeBack is not enabled for this project.\n"
"Please enable MergeBack in the project settings\n" "Please enable MergeBack in the project settings\n"
"dialog and re-save the project file and the code."); "dialog and re-save the project file and the code.");
}
recursion_lock = false;
return 0; return 0;
} }
@ -633,7 +643,7 @@ int mergeback_code_files(Project &proj)
#if 1 #if 1
if (!Fluid.batch_mode) { if (!Fluid.batch_mode) {
// Depending on the workflow in interactive mode, an external copy of // Depending on the workflow in interactive mode, an external copy of
// Fluid may have written the source code elswhere (e.g. in a CMake setup). // Fluid may have written the source code elsewhere (e.g. in a CMake setup).
// Fluid tries to keep track of the last write location of a source file // Fluid tries to keep track of the last write location of a source file
// matching a project, and uses that location instead. // matching a project, and uses that location instead.
// TODO: this is not working as expected yet. // TODO: this is not working as expected yet.
@ -656,15 +666,32 @@ int mergeback_code_files(Project &proj)
} }
if (!Fluid.batch_mode) proj.leave_project_dir(); if (!Fluid.batch_mode) proj.leave_project_dir();
if (feedback & Mergeback::CHATTY) {
if (c==0) fl_message("Comparing\n \"%s\"\nto\n \"%s\"\n\n" if (c==0) fl_message("Comparing\n \"%s\"\nto\n \"%s\"\n\n"
"MergeBack found no external modifications\n" "MergeBack found no external modifications\n"
"in the source code.", "in the source code.",
code_filename.c_str(), proj_filename.c_str()); code_filename.c_str(), proj_filename.c_str());
if (c==-2) fl_message("No corresponding source code file found."); if (c==-2) fl_message("No corresponding source code file found.");
}
recursion_lock = false;
return c; return c;
} }
void mergeback_cb(Fl_Widget *, void *) { void mergeback_cb(Fl_Widget *, void *) {
mergeback_code_files(Fluid.proj); mergeback_code_files(Fluid.proj, Mergeback::CHATTY);
} }
void mergeback_on_load() {
mergeback_code_files(Fluid.proj, Mergeback::QUIET);
}
static int app_event_handler(int event) {
if (event == FL_APP_ACTIVATE) {
mergeback_code_files(Fluid.proj, Mergeback::QUIET);
}
return 0;
}
void start_auto_mergeback() {
Fl::add_handler(app_event_handler);
}

View File

@ -42,6 +42,7 @@ class Mergeback
enum class Task { enum class Task {
ANALYSE = 0, INTERACTIVE, APPLY, APPLY_IF_SAFE = 3 ANALYSE = 0, INTERACTIVE, APPLY, APPLY_IF_SAFE = 3
}; };
enum Feedback { QUIET = 0, CHATTY = 1 };
protected: protected:
/// Apply mergeback for this project. /// Apply mergeback for this project.
Project &proj_; Project &proj_;
@ -88,5 +89,8 @@ extern int merge_back(const std::string &s, const std::string &p, int task);
} // namespace proj } // namespace proj
} // namespace fld } // namespace fld
extern void start_auto_mergeback();
extern void mergeback_on_load();
#endif // FLUID_PROJ_MERGEBACK_H #endif // FLUID_PROJ_MERGEBACK_H