mirror of
https://github.com/fltk/fltk.git
synced 2026-08-17 16:43:10 +08:00
Fluid: add MErgeBack support for the command line
This commit is contained in:
+25
-1
@@ -22,6 +22,7 @@
|
||||
#include "proj/mergeback.h"
|
||||
#include "app/Menu.h"
|
||||
#include "app/shell_command.h"
|
||||
#include "proj/mergeback.h"
|
||||
#include "proj/undo.h"
|
||||
#include "io/Project_Reader.h"
|
||||
#include "io/Project_Writer.h"
|
||||
@@ -207,6 +208,29 @@ int Application::run(int argc,char **argv) {
|
||||
proj.strings_file_set = 1;
|
||||
proj.strings_file_name = args.strings_filename;
|
||||
}
|
||||
if (args.mergeback_mode > 0) {
|
||||
// ANALYSE = 0, INTERACTIVE, APPLY, APPLY_IF_SAFE
|
||||
// -mb=info
|
||||
proj::Mergeback::Task task = proj::Mergeback::Task::INFO;
|
||||
// -mb=ask
|
||||
if (args.mergeback_mode == 2) task = proj::Mergeback::Task::INTERACTIVE;
|
||||
// -mb=apply
|
||||
if (args.mergeback_mode == 3) task = proj::Mergeback::Task::APPLY_IF_SAFE;
|
||||
int ret = merge_back(
|
||||
proj,
|
||||
proj.codefile_path() + proj.codefile_name(),
|
||||
proj.projectfile_path() + proj.projectfile_name(),
|
||||
task
|
||||
);
|
||||
if (ret < 0) {
|
||||
printf("\n");
|
||||
fluid::alert("Fluid", "Operation cancelled.");
|
||||
exit(1); // error in mergeback
|
||||
} else if (ret > 0) {
|
||||
printf("\n");
|
||||
fluid::message("Fluid", "Mergeback applied.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.update_file) { // fluid -u
|
||||
@@ -228,6 +252,7 @@ int Application::run(int argc,char **argv) {
|
||||
|
||||
proj.set_modflag(0);
|
||||
proj.undo.clear();
|
||||
if (!proj.tree.empty()) mergeback_on_load();
|
||||
|
||||
// Set (but do not start) timer callback for external editor updates
|
||||
ExternalCodeEditor::set_update_timer_callback(external_editor_timer);
|
||||
@@ -503,7 +528,6 @@ bool Application::open_project_file(const std::string &filename_arg) {
|
||||
// clear the project and merge a file by the given name
|
||||
new_project();
|
||||
bool success = proj.load_or_merge(new_filename);
|
||||
if (success) mergeback_on_load();
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,19 @@ int Args::arg(int argc, char** argv, int& i) {
|
||||
Fluid.batch_mode++;
|
||||
i += 2; return 2;
|
||||
}
|
||||
//-mb=info, -mb=ask, -mb=apply
|
||||
if (strcmp(argv[i], "-mb=info")==0) {
|
||||
mergeback_mode = 1; // inform
|
||||
i++; return 1;
|
||||
}
|
||||
if (strcmp(argv[i], "-mb=ask")==0) {
|
||||
mergeback_mode = 2; // ask
|
||||
i++; return 1;
|
||||
}
|
||||
if (strcmp(argv[i], "-mb=apply")==0) {
|
||||
mergeback_mode = 3; // apply
|
||||
i++; return 1;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
if ((i+1 < argc) && (strcmp(argv[i], "--autodoc") == 0)) {
|
||||
autodoc_path = argv[i+1];
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
/// Make all output file paths relative to the .fl project file path
|
||||
/// instead of the current working directory, if set.
|
||||
bool project_relative { false }; // fluid -pr
|
||||
/// 0=ignore, 1=inform, 2=ask, 3=apply
|
||||
int mergeback_mode { 0 }; // fluid -mb=info, -mb=ask, -mb=apply
|
||||
/// Constructor.
|
||||
Args() = default;
|
||||
// Load args from command line into variables.
|
||||
|
||||
@@ -61,6 +61,24 @@
|
||||
- Fluid will analyze the `.cxx` file, detect changes within the marked
|
||||
sections, and offer to merge them back into the `.fl` project file.
|
||||
|
||||
<H2>MergeBack Command Line Options</H2>
|
||||
|
||||
When running in batch mode with `-c` or `-cs`, Fluid offers three command-line
|
||||
options to control MergeBack. If none of these options is specified, MergeBack
|
||||
is disabled and the C++ file is overwritten without warning.
|
||||
|
||||
- `-mb=info`: Analyze the MergeBack status. If no changes are detected, Fluid
|
||||
writes the C++ files as requested. If changes are found, Fluid prints an
|
||||
information message, does not write any files, and exits with status code `1`.
|
||||
- `-mb=apply`: If changes can be merged back safely, Fluid applies them, writes
|
||||
the project file, and then overwrites the C++ file. If unsafe changes are
|
||||
detected that cannot be merged back, Fluid exits with error code `1` without
|
||||
writing any files. If no changes are detected, Fluid simply writes the C++
|
||||
files as requested.
|
||||
- `-mb=ask`: Same as `-mb=apply`, but Fluid asks for confirmation before
|
||||
overwriting the project file. This option may require a key press to
|
||||
continue, so do not use it in automated scripts.
|
||||
|
||||
<H2>Things to Keep in Mind</H2>
|
||||
|
||||
- **Scope of MergeBack:** Currently, MergeBack supports merging changes
|
||||
|
||||
+6
-3
@@ -179,20 +179,23 @@ int fluid::choice(const std::string &title, const std::string &message,
|
||||
printf("%s: %s\n", title.c_str(), message.c_str());
|
||||
// Write all available options
|
||||
printf("Options: ");
|
||||
int default_index = (option.size() > 1) ? 1 : 0;
|
||||
bool comma = false;
|
||||
int ix = 0;
|
||||
for (const auto &opt : option) {
|
||||
if (comma) printf(", ");
|
||||
printf("%s[%c]", opt.label.c_str(), opt.key);
|
||||
int key = (ix==default_index) ? fl_ascii_toupper(opt.key) : fl_ascii_tolower(opt.key);
|
||||
printf("%s[%c]", opt.label.c_str(), key);
|
||||
comma = true;
|
||||
ix++;
|
||||
}
|
||||
printf(": ");
|
||||
fflush(stdout);
|
||||
// Loop until we receive a supported key, or the user hits <esc> or <return>
|
||||
int default_index = (option.size() > 1) ? 1 : 0;
|
||||
for (;;) {
|
||||
int key = read_console_key();
|
||||
if (key == 27) { printf("<esc>\n"); return msg::ESC; }
|
||||
if (key == '\r' || key == '\n') { printf("<return>\n"); return default_index; }
|
||||
if (key == '\r' || key == '\n') { printf("%s\n", option[default_index].label.c_str()); return default_index; }
|
||||
for (size_t i = 0; i < option.size(); ++i) {
|
||||
if (fl_ascii_tolower(key) == fl_ascii_tolower((unsigned char)option[i].key)) {
|
||||
printf("%s\n", option[i].label.c_str());
|
||||
|
||||
+41
-22
@@ -102,7 +102,7 @@ using namespace fluid::proj;
|
||||
\return -2 if no code file was found
|
||||
\return see above
|
||||
*/
|
||||
int merge_back(Project &proj, const std::string &s, const std::string &p, Mergeback::Task task) {
|
||||
int fluid::merge_back(Project &proj, const std::string &s, const std::string &p, Mergeback::Task task) {
|
||||
if (proj.write_mergeback_data) {
|
||||
Mergeback mergeback(proj);
|
||||
return mergeback.merge_back(s, p, task);
|
||||
@@ -180,7 +180,7 @@ std::string Mergeback::read_and_unindent_block(long start, long end) {
|
||||
\return -1 if the user wants to cancel or an error occurred or an issue was presented
|
||||
(message or choice dialog was shown)
|
||||
*/
|
||||
int Mergeback::ask_user_to_merge(const std::string &code_filename, const std::string &project_filename) {
|
||||
int Mergeback::ask_user_to_merge(const std::string &code_filename, const std::string &project_filename, bool info_only) {
|
||||
if (tag_error) {
|
||||
fluid_message("Comparing\n \"%s\"\nto\n \"%s\"\n\n"
|
||||
"MergeBack found an error in line %d while reading tags\n"
|
||||
@@ -225,15 +225,23 @@ int Mergeback::ask_user_to_merge(const std::string &code_filename, const std::st
|
||||
num_changed_structure, num_possible_override);
|
||||
return -1;
|
||||
} else {
|
||||
msg += "\n\nClick Cancel to abort the MergeBack operation.\n"
|
||||
"Click Merge to merge all code changes back into\n"
|
||||
"the open project.";
|
||||
int c = fluid_choice(msg.c_str(), "Cancel", "Merge", nullptr,
|
||||
code_filename.c_str(), project_filename.c_str(),
|
||||
num_changed_code, num_uid_not_found,
|
||||
num_changed_structure, num_possible_override);
|
||||
if (c==0) return -1;
|
||||
return 1;
|
||||
if (info_only) {
|
||||
fluid_message(msg.c_str(),
|
||||
code_filename.c_str(), project_filename.c_str(),
|
||||
num_changed_code, num_uid_not_found,
|
||||
num_changed_structure, num_possible_override);
|
||||
return -1;
|
||||
} else {
|
||||
msg += "\n\nClick Cancel to abort the MergeBack operation.\n"
|
||||
"Click Merge to merge all code changes back into\n"
|
||||
"the open project.";
|
||||
int c = fluid_choice(msg.c_str(), "Cancel", "Merge", nullptr,
|
||||
code_filename.c_str(), project_filename.c_str(),
|
||||
num_changed_code, num_uid_not_found,
|
||||
num_changed_structure, num_possible_override);
|
||||
if (c != 1) return -1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,8 +634,7 @@ int Mergeback::apply() {
|
||||
|
||||
/** Dispatch the MergeBack into analysis, interactive, or apply directly.
|
||||
\param[in] s source code filename and path
|
||||
\param[in] task one of FD_MERGEBACK_ANALYSE, FD_MERGEBACK_INTERACTIVE,
|
||||
FD_MERGEBACK_APPLY_IF_SAFE, or FD_MERGEBACK_APPLY
|
||||
\param[in] task one of ANALYSE, INFO, INTERACTIVE, APPLY, or APPLY_IF_SAFE
|
||||
\return -1 if an error was found in a tag
|
||||
\return -2 if no code file was found
|
||||
\return See more at ::merge_back(const std::string &s, int task).
|
||||
@@ -637,24 +644,32 @@ int Mergeback::merge_back(const std::string &s, const std::string &p, Task task)
|
||||
code = fl_fopen(s.c_str(), "rb");
|
||||
if (!code) return -2;
|
||||
do { // no actual loop, just make sure we close the code file
|
||||
if (task == Task::ANALYSE) {
|
||||
if ((task == Task::ANALYSE) || (task == Task::INFO)) {
|
||||
analyse();
|
||||
if (tag_error) {ret = -1; break; }
|
||||
if (num_changed_structure) ret |= 1;
|
||||
if (num_changed_code) ret |= 2;
|
||||
if (num_uid_not_found) ret |= 4;
|
||||
if (num_possible_override) ret |= 8;
|
||||
if (num_changed_structure) ret |= 2;
|
||||
if (num_changed_code) ret |= 4;
|
||||
if (num_uid_not_found) ret |= 8;
|
||||
if (num_possible_override) ret |= 16;
|
||||
if (task == Task::ANALYSE) break;
|
||||
}
|
||||
if (task == Task::INFO) {
|
||||
// tell findings
|
||||
ret = ask_user_to_merge(s, p, true /* info_only */);
|
||||
break;
|
||||
}
|
||||
if (task == Task::INTERACTIVE) {
|
||||
analyse();
|
||||
ret = ask_user_to_merge(s, p);
|
||||
if (ret != 1)
|
||||
return ret;
|
||||
break;
|
||||
task = Task::APPLY; // fall through
|
||||
}
|
||||
if (task == Task::APPLY_IF_SAFE) {
|
||||
analyse();
|
||||
if (Fluid.batch_mode) {
|
||||
ask_user_to_merge(s, p, true /* info_only */);
|
||||
}
|
||||
if (tag_error || num_changed_structure || num_possible_override) {
|
||||
ret = -1;
|
||||
break;
|
||||
@@ -668,9 +683,13 @@ int Mergeback::merge_back(const std::string &s, const std::string &p, Task task)
|
||||
if (task == Task::APPLY) {
|
||||
ret = apply();
|
||||
if (ret == 1) {
|
||||
proj_.set_modflag(1);
|
||||
redraw_browser();
|
||||
load_panel();
|
||||
if (Fluid.batch_mode) {
|
||||
proj_.save();
|
||||
} else {
|
||||
proj_.set_modflag(1);
|
||||
redraw_browser();
|
||||
load_panel();
|
||||
}
|
||||
}
|
||||
ret = 1; // avoid message box in caller
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
END_OF_LIST_ = UNUSED_
|
||||
};
|
||||
enum class Task {
|
||||
ANALYSE = 0, INTERACTIVE, APPLY, APPLY_IF_SAFE
|
||||
ANALYSE = 0, INFO, INTERACTIVE, APPLY, APPLY_IF_SAFE
|
||||
};
|
||||
enum Feedback { QUIET = 0, CHATTY = 1 };
|
||||
protected:
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
Mergeback(Project &proj);
|
||||
~Mergeback();
|
||||
int merge_back(const std::string &s, const std::string &p, Task task);
|
||||
int ask_user_to_merge(const std::string &s, const std::string &p);
|
||||
int ask_user_to_merge(const std::string &s, const std::string &p, bool info_only=false);
|
||||
int analyse();
|
||||
int apply();
|
||||
static void print_tag(FILE *out, Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc);
|
||||
@@ -98,10 +98,14 @@ public:
|
||||
extern int merge_back(const std::string &s, const std::string &p, int task);
|
||||
|
||||
} // namespace proj
|
||||
|
||||
extern int merge_back(Project &proj, const std::string &s, const std::string &p, proj::Mergeback::Task task);
|
||||
|
||||
} // namespace fluid
|
||||
|
||||
extern void start_auto_mergeback();
|
||||
extern void mergeback_on_load();
|
||||
|
||||
|
||||
#endif // FLUID_PROJ_MERGEBACK_H
|
||||
|
||||
|
||||
Reference in New Issue
Block a user