Fluid: write code files only when the actually change (STR 1859) (#1406)
Build and Test / build-linux (push) Has been cancelled
Build and Test / build-wayland (push) Has been cancelled
Build and Test / build-macos (push) Has been cancelled
Build and Test / build-windows (push) Has been cancelled

This prevents unnecessary file updates when content hasn't changed,
reducing recompilation time in projects that depend on fluid-generated files.
Uses std::ostringstream before deciding if file differs.
This commit is contained in:
Matthias Melcher
2026-04-13 17:11:15 +02:00
committed by GitHub
parent 15448c45b0
commit 34c7120330
8 changed files with 348 additions and 222 deletions
+1 -1
View File
@@ -172,11 +172,11 @@ int Application::run(int argc,char **argv) {
Fluid.layout_list.read(preferences, fld::Tool_Store::USER);
main_window->show(argc,argv);
toggle_widget_bin();
toggle_codeview_cb(nullptr,nullptr);
if (!c && openlast_button->value() && history.abspath[0][0] && args.autodoc_path.empty()) {
// Open previous file when no file specified...
open_project_file(history.abspath[0]);
}
toggle_codeview_cb(nullptr,nullptr);
}
proj.undo.suspend();
if (c && !fld::io::read_file(proj, c,0)) {
+175 -77
View File
@@ -151,7 +151,7 @@ int Code_Writer::write_h_once(const char *format, ...) {
if (text_in_header.find(buf) != text_in_header.end()) {
return 0;
}
fprintf(header_file, "%s\n", buf);
header_buffer << buf << "\n";
text_in_header.insert(buf);
return 1;
}
@@ -384,12 +384,25 @@ void Code_Writer::write_cc(const char *indent, int n, const char *c, const char
Print a formatted line to the header file.
\param[in] format printf-style formatting text, followed by a vararg list
*/
void Code_Writer::write_h(const char* format,...) {
void Code_Writer::write_h(const char* format,...)
{
if (varused_test) return;
va_list args;
va_start(args, format);
vfprintf(header_file, format, args);
int n = vsnprintf(block_buffer_, block_buffer_size_, format, args);
if (n > block_buffer_size_) {
// Buffer was too small, reallocate and try again with the copy
block_buffer_size_ = n + 128;
if (block_buffer_) ::free(block_buffer_);
block_buffer_ = (char*)::malloc(block_buffer_size_+1);
va_end(args);
va_start(args, format);
n = vsnprintf(block_buffer_, block_buffer_size_, format, args);
}
va_end(args);
header_buffer << block_buffer_;
}
/**
@@ -491,11 +504,11 @@ bool is_comment_before_class_member(Node *q) {
\return pointer to the next sibling
*/
Node* Code_Writer::write_static(Node* p) {
if (write_codeview) p->header_static_start = (int)ftell(header_file);
if (write_codeview) p->code_static_start = (int)ftell(code_file);
if (write_codeview) p->header_static_start = header_pos();
if (write_codeview) p->code_static_start = code_pos();
p->write_static(*this);
if (write_codeview) p->code_static_end = (int)ftell(code_file);
if (write_codeview) p->header_static_end = (int)ftell(header_file);
if (write_codeview) p->code_static_end = code_pos();
if (write_codeview) p->header_static_end = header_pos();
Node* q;
for (q = p->next; q && q->level > p->level;) {
@@ -516,11 +529,11 @@ Node* Code_Writer::write_code(Node* p) {
// write all code that comes before the children code
// (but don't write the last comment until the very end)
if (!(p==Fluid.proj.tree.last && p->is_a(Type::Comment))) {
if (write_codeview) p->code1_start = (int)ftell(code_file);
if (write_codeview) p->header1_start = (int)ftell(header_file);
if (write_codeview) p->code1_start = code_pos();
if (write_codeview) p->header1_start = header_pos();
p->write_code1(*this);
if (write_codeview) p->code1_end = (int)ftell(code_file);
if (write_codeview) p->header1_end = (int)ftell(header_file);
if (write_codeview) p->code1_end = code_pos();
if (write_codeview) p->header1_end = header_pos();
}
// recursively write the code of all children
Node* q;
@@ -539,11 +552,11 @@ Node* Code_Writer::write_code(Node* p) {
}
// write all code that come after the children
if (write_codeview) p->code2_start = (int)ftell(code_file);
if (write_codeview) p->header2_start = (int)ftell(header_file);
if (write_codeview) p->code2_start = code_pos();
if (write_codeview) p->header2_start = header_pos();
p->write_code2(*this);
if (write_codeview) p->code2_end = (int)ftell(code_file);
if (write_codeview) p->header2_end = (int)ftell(header_file);
if (write_codeview) p->code2_end = code_pos();
if (write_codeview) p->header2_end = header_pos();
for (q = p->next; q && q->level > p->level;) {
if (is_class_member(q) || is_comment_before_class_member(q)) {
@@ -561,11 +574,11 @@ Node* Code_Writer::write_code(Node* p) {
} else {
for (q = p->next; q && q->level > p->level;) q = write_code(q);
// write all code that come after the children
if (write_codeview) p->code2_start = (int)ftell(code_file);
if (write_codeview) p->header2_start = (int)ftell(header_file);
if (write_codeview) p->code2_start = code_pos();
if (write_codeview) p->header2_start = header_pos();
p->write_code2(*this);
if (write_codeview) p->code2_end = (int)ftell(code_file);
if (write_codeview) p->header2_end = (int)ftell(header_file);
if (write_codeview) p->code2_end = code_pos();
if (write_codeview) p->header2_end = header_pos();
}
return q;
}
@@ -573,7 +586,8 @@ Node* Code_Writer::write_code(Node* p) {
/**
Write the source and header files for the current design.
If the files already exist, they will be overwritten.
If the files already exist, they will be overwritten only if the content
has changed. This conservative approach helps reduce unnecessary recompilation.
\note There is no true error checking here.
@@ -587,18 +601,13 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) {
indentation = 0;
current_class = nullptr;
current_widget_class = nullptr;
if (!s) code_file = stdout;
else {
FILE *f = fl_fopen(s, "wb");
if (!f) return 0;
code_file = f;
}
if (!t) header_file = stdout;
else {
FILE *f = fl_fopen(t, "wb");
if (!f) {fclose(code_file); return 0;}
header_file = f;
}
// Always use string stream buffers for output
code_buffer.str("");
code_buffer.clear();
header_buffer.str("");
header_buffer.clear();
// Remember the last code file location for MergeBack
if (s && proj_.write_mergeback_data && !to_codeview) {
std::string filename = proj_.projectfile_path() + proj_.projectfile_name();
@@ -613,21 +622,21 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) {
Node* first_node = Fluid.proj.tree.first;
if (first_node && first_node->is_a(Type::Comment)) {
if (write_codeview) {
first_node->code1_start = first_node->code2_start = (int)ftell(code_file);
first_node->header1_start = first_node->header2_start = (int)ftell(header_file);
first_node->code1_start = first_node->code2_start = code_pos();
first_node->header1_start = first_node->header2_start = header_pos();
}
// it is ok to write non-recursive code here, because comments have no children or code2 blocks
first_node->write_code1(*this);
if (write_codeview) {
first_node->code1_end = first_node->code2_end = (int)ftell(code_file);
first_node->header1_end = first_node->header2_end = (int)ftell(header_file);
first_node->code1_end = first_node->code2_end = code_pos();
first_node->header1_end = first_node->header2_end = header_pos();
}
first_node = first_node->next;
}
const char *hdr = "\
// generated by Fast Light User Interface Designer (fluid) version %.4f\n\n";
fprintf(header_file, hdr, FL_VERSION);
write_h(hdr, FL_VERSION);
crc_printf(hdr, FL_VERSION);
{
// Creating the include guard is more involved than it seems at first glance.
@@ -672,8 +681,8 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) {
}
macro_name_str = macro_name.str();
}
fprintf(header_file, "#ifndef %s\n", macro_name_str.c_str());
fprintf(header_file, "#define %s\n", macro_name_str.c_str());
write_h("#ifndef %s\n", macro_name_str.c_str());
write_h("#define %s\n", macro_name_str.c_str());
}
if (proj_.avoid_early_includes==0) {
@@ -746,31 +755,42 @@ int Code_Writer::write_code(const char *s, const char *t, bool to_codeview) {
p = write_code(p);
}
if (!s) return 1;
fprintf(header_file, "#endif\n");
write_h("#endif\n");
Node* last_node = Fluid.proj.tree.last;
if (last_node && (last_node != Fluid.proj.tree.first) && last_node->is_a(Type::Comment)) {
if (write_codeview) {
last_node->code1_start = last_node->code2_start = (int)ftell(code_file);
last_node->header1_start = last_node->header2_start = (int)ftell(header_file);
last_node->code1_start = last_node->code2_start = code_pos();
last_node->header1_start = last_node->header2_start = header_pos();
}
last_node->write_code1(*this);
if (write_codeview) {
last_node->code1_end = last_node->code2_end = (int)ftell(code_file);
last_node->header1_end = last_node->header2_end = (int)ftell(header_file);
last_node->code1_end = last_node->code2_end = code_pos();
last_node->header1_end = last_node->header2_end = header_pos();
}
}
int x = 0, y = 0;
if (code_file != stdout)
x = fclose(code_file);
code_file = nullptr;
if (header_file != stdout)
y = fclose(header_file);
header_file = nullptr;
return x >= 0 && y >= 0;
// For codeview mode, strings are available via code_string() / header_string()
if (write_codeview)
return 1;
// Write code output: to file if filename provided, to stdout otherwise
bool code_ok = true;
if (s) {
code_ok = write_file_if_changed(s, code_buffer.str());
} else {
fputs(code_buffer.str().c_str(), stdout);
}
// Write header output: to file if filename provided, to stdout otherwise
bool header_ok = true;
if (t) {
header_ok = write_file_if_changed(t, header_buffer.str());
} else {
fputs(header_buffer.str().c_str(), stdout);
}
return code_ok && header_ok ? 1 : 0;
}
@@ -819,7 +839,7 @@ Code_Writer::~Code_Writer()
*/
void Code_Writer::tag(proj::Mergeback::Tag prev_type, proj::Mergeback::Tag next_type, unsigned short uid) {
if (proj_.write_mergeback_data) {
Mergeback::print_tag(code_file, prev_type, next_type, uid, (uint32_t)block_crc_);
code_buffer << Mergeback::format_tag(prev_type, next_type, uid, (uint32_t)block_crc_);
}
block_crc_ = crc32(0, nullptr, 0);
}
@@ -866,10 +886,10 @@ void Code_Writer::crc_add(const void *data, int n) {
block_crc_ = block_crc(data, n, block_crc_, &block_line_start_);
}
/** Write formatted text to the code file.
/** Write formatted text to the code buffer.
If MergeBack is enabled, the CRC calculation is continued.
\param[in] format printf style formatting string
\return see fprintf(FILE *, *const char*, ...)
\return number of characters formatted
*/
int Code_Writer::crc_printf(const char *format, ...) {
va_list args;
@@ -879,51 +899,129 @@ int Code_Writer::crc_printf(const char *format, ...) {
return ret;
}
/** Write formatted text to the code file.
/** Write formatted text to the code buffer.
If MergeBack is enabled, the CRC calculation is continued.
\param[in] format printf style formatting string
\param[in] args list of arguments
\return see fprintf(FILE *, *const char*, ...)
\return number of characters formatted
*/
int Code_Writer::crc_vprintf(const char *format, va_list args) {
if (proj_.write_mergeback_data) {
int n = vsnprintf(block_buffer_, block_buffer_size_, format, args);
if (n > block_buffer_size_) {
block_buffer_size_ = n + 128;
if (block_buffer_) ::free(block_buffer_);
block_buffer_ = (char*)::malloc(block_buffer_size_+1);
n = vsnprintf(block_buffer_, block_buffer_size_, format, args);
}
crc_add(block_buffer_, n);
return fputs(block_buffer_, code_file);
} else {
return vfprintf(code_file, format, args);
// Make a copy of args in case we need to call vsnprintf twice
// (the first call consumes args on some platforms)
va_list args_copy;
va_copy(args_copy, args);
int n = vsnprintf(block_buffer_, block_buffer_size_, format, args);
if (n > block_buffer_size_) {
// Buffer was too small, reallocate and try again with the copy
block_buffer_size_ = n + 128;
if (block_buffer_) ::free(block_buffer_);
block_buffer_ = (char*)::malloc(block_buffer_size_+1);
n = vsnprintf(block_buffer_, block_buffer_size_, format, args_copy);
}
va_end(args_copy);
if (proj_.write_mergeback_data) {
crc_add(block_buffer_, n);
}
code_buffer << block_buffer_;
return n;
}
/** Write some text to the code file.
/** Write some text to the code buffer.
If MergeBack is enabled, the CRC calculation is continued.
\param[in] text any text, no requirements to end in a newline or such
\return see fputs(const char*, FILE*)
\return always 0
*/
int Code_Writer::crc_puts(const char *text) {
if (proj_.write_mergeback_data) {
crc_add(text);
}
return fputs(text, code_file);
code_buffer << text;
return 0;
}
/** Write a single ASCII character to the code file.
/** Write a single ASCII character to the code buffer.
If MergeBack is enabled, the CRC calculation is continued.
\note to write UTF-8 characters, use Code_Writer::crc_puts(const char *text)
\param[in] c any character between 0 and 127 inclusive
\return see fputc(int, FILE*)
\return the character written
*/
int Code_Writer::crc_putc(int c) {
if (proj_.write_mergeback_data) {
uchar uc = (uchar)c;
crc_add(&uc, 1);
}
return fputc(c, code_file);
code_buffer << (char)c;
return c;
}
/**
Check if the content of a file matches a given string.
\param[in] filename path to the file to compare
\param[in] content the string content to compare with
\return true if the file exists and its content matches exactly, false otherwise
*/
bool Code_Writer::file_content_matches(const char *filename, const std::string &content) {
FILE *f = fl_fopen(filename, "rb");
if (!f) {
return false; // File doesn't exist
}
// Get file size
if (fseek(f, 0, SEEK_END) != 0) {
fclose(f);
return false; // Seek error
}
long file_size = ftell(f);
if (file_size < 0) {
fclose(f);
return false; // ftell error
}
if (fseek(f, 0, SEEK_SET) != 0) {
fclose(f);
return false; // Seek error
}
// Quick check: if sizes don't match, content is different
if ((size_t)file_size != content.size()) {
fclose(f);
return false;
}
// Read file content and compare
std::string file_content((size_t)file_size, '\0');
size_t bytes_read = fread(&file_content[0], 1, (size_t)file_size, f);
fclose(f);
if (bytes_read != (size_t)file_size) {
return false; // Read error
}
return file_content == content;
}
/**
Write content to a file only if the content differs from the existing file.
This is a conservative write that avoids unnecessary file modifications,
which helps reduce recompilation in build systems.
\param[in] filename path to the file to write
\param[in] content the string content to write
\return true if the file was written or already up-to-date, false on write error
*/
bool Code_Writer::write_file_if_changed(const char *filename, const std::string &content) {
// If content matches, no need to write
if (file_content_matches(filename, content)) {
return true; // File is already up-to-date
}
// Content differs or file doesn't exist - write the new content
FILE *f = fl_fopen(filename, "wb");
if (!f) {
return false; // Cannot open file for writing
}
size_t written = fwrite(content.c_str(), 1, content.size(), f);
int result = fclose(f);
return (written == content.size()) && (result == 0);
}
+18 -4
View File
@@ -26,6 +26,7 @@
#include <string>
#include <set>
#include <map>
#include <sstream>
class Node;
struct Fd_Identifier_Tree;
@@ -46,10 +47,10 @@ private:
/// Link Code_Writer class to the project.
Project &proj_;
/// file pointer for the C++ code file
FILE *code_file = nullptr;
/// file pointer for the C++ header file
FILE *header_file = nullptr;
/// string stream buffer for generating C++ code file content
std::ostringstream code_buffer;
/// string stream buffer for generating C++ header file content
std::ostringstream header_buffer;
/// tree of unique but human-readable identifiers
std::map<std::string, void*> unique_id_list { };
@@ -75,6 +76,14 @@ private:
int crc_puts(const char *text);
int crc_putc(int c);
bool file_content_matches(const char *filename, const std::string &content);
bool write_file_if_changed(const char *filename, const std::string &content);
/// Return the current write position in the code output stream.
int code_pos() { return (int)code_buffer.tellp(); }
/// Return the current write position in the header output stream.
int header_pos() { return (int)header_buffer.tellp(); }
public:
/// current level of source code indentation
int indentation = 0;
@@ -116,6 +125,11 @@ public:
int write_code(const char *cfile, const char *hfile, bool to_codeview=false);
void write_public(int state); // writes pubic:/private: as needed
/// Return the generated source code as a string (valid after write_code() with to_codeview=true).
std::string code_string() const { return code_buffer.str(); }
/// Return the generated header code as a string (valid after write_code() with to_codeview=true).
std::string header_string() const { return header_buffer.str(); }
void tag(proj::Mergeback::Tag prev_type, proj::Mergeback::Tag next_type, unsigned short uid);
static unsigned long block_crc(const void *data, int n=-1, unsigned long in_crc=0, bool *inout_line_start=nullptr);
+48 -60
View File
@@ -26,8 +26,6 @@
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Button.H>
#include "../src/flstring.h"
static char *cv_source_filename = nullptr;
static char *cv_header_filename = nullptr;
static char *cv_design_filename = nullptr;
int cv_code_choice;
extern void select_only(Node *o);
@@ -147,70 +145,60 @@ void update_codeview_position_cb(class Fl_Tabs*, void*) {
}
/**
Generate a header, source, strings, or design file in a temporary directory
and load those into the Code Viewer widgets.
Generate a header, source, strings, or design file and load the content into
the Code Viewer widgets. Source and header code are generated entirely in
memory (via ostringstream) and pushed directly into the text buffers — no
temporary files are written or read back for those tabs.
*/
void update_codeview_cb(class Fl_Button*, void*) {
if (!codeview_panel || !codeview_panel->visible())
return;
return;
if (!cv_source_filename) {
cv_source_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_source_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_source_filename, "codeview_tmp.cxx", FL_PATH_MAX);
}
if (!cv_header_filename) {
cv_header_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_header_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_header_filename, "codeview_tmp.h", FL_PATH_MAX);
}
if (!cv_design_filename) {
cv_design_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX);
}
if (!cv_design_filename) {
cv_design_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX);
}
if (cv_project->visible_r()) {
fld::io::write_file(Fluid.proj, cv_design_filename, false, true);
int top = cv_project->top_line();
cv_project->buffer()->loadfile(cv_design_filename);
cv_project->scroll(top, 0);
} else if (cv_strings->visible_r()) {
static const char *exts[] = { ".txt", ".po", ".msg" };
char fn[FL_PATH_MAX+1];
fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(fn, "strings", FL_PATH_MAX);
fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast<int>(Fluid.proj.i18n.type)]);
fld::io::write_strings(Fluid.proj, fn);
int top = cv_strings->top_line();
cv_strings->buffer()->loadfile(fn);
cv_strings->scroll(top, 0);
} else if (cv_source->visible_r() || cv_header->visible_r()) {
std::string code_file_name_bak = Fluid.proj.code_file_name;
Fluid.proj.code_file_name = cv_source_filename;
std::string header_file_name_bak = Fluid.proj.header_file_name;
Fluid.proj.header_file_name = cv_header_filename;
// generate the code and load the files
fld::io::Code_Writer f(Fluid.proj);
// generate files
if (f.write_code(cv_source_filename, cv_header_filename, true))
{
// load file into source editor
int pos = cv_source->top_line();
cv_source->buffer()->loadfile(cv_source_filename);
cv_source->scroll(pos, 0);
// load file into header editor
pos = cv_header->top_line();
cv_header->buffer()->loadfile(cv_header_filename);
cv_header->scroll(pos, 0);
// update the source code highlighting
update_codeview_position();
}
Fluid.proj.code_file_name = code_file_name_bak;
Fluid.proj.header_file_name = header_file_name_bak;
if (cv_project->visible_r()) {
fld::io::write_file(Fluid.proj, cv_design_filename, false, true);
int top = cv_project->top_line();
cv_project->buffer()->loadfile(cv_design_filename);
cv_project->scroll(top, 0);
} else if (cv_strings->visible_r()) {
static const char *exts[] = { ".txt", ".po", ".msg" };
char fn[FL_PATH_MAX+1];
fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(fn, "strings", FL_PATH_MAX);
fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast<int>(Fluid.proj.i18n.type)]);
fld::io::write_strings(Fluid.proj, fn);
int top = cv_strings->top_line();
cv_strings->buffer()->loadfile(fn);
cv_strings->scroll(top, 0);
} else if (cv_source->visible_r() || cv_header->visible_r()) {
// Generate code into in-memory ostringstream buffers (no temp files).
fld::io::Code_Writer f(Fluid.proj);
std::string code_filename = Fluid.proj.codefile_path() + Fluid.proj.codefile_name();
std::string header_filename = Fluid.proj.headerfile_path() + Fluid.proj.headerfile_name();
if (Fluid.proj.proj_filename)
Fluid.proj.enter_project_dir();
// Setting code_view to true write code files directly into a string buffer
// and does not write over the code files on disk.
if (f.write_code(code_filename.c_str(), header_filename.c_str(), true))
{
// Push generated text directly into the editor buffers.
int pos = cv_source->top_line();
cv_source->buffer()->text(f.code_string().c_str());
cv_source->scroll(pos, 0);
pos = cv_header->top_line();
cv_header->buffer()->text(f.header_string().c_str());
cv_header->scroll(pos, 0);
// update the source code highlighting
update_codeview_position();
}
if (Fluid.proj.proj_filename)
Fluid.proj.leave_project_dir();
}
}
/**
+49 -65
View File
@@ -48,12 +48,6 @@ decl {\#include <FL/Fl_Button.H>} {private local
decl {\#include "../src/flstring.h"} {private local
}
decl {char *cv_source_filename = nullptr;} {private local
}
decl {char *cv_header_filename = nullptr;} {private local
}
decl {char *cv_design_filename = nullptr;} {private local
}
@@ -178,69 +172,59 @@ Function {update_codeview_position_cb(class Fl_Tabs*, void*)} {
}
Function {update_codeview_cb(class Fl_Button*, void*)} {
comment {Generate a header, source, strings, or design file in a temporary directory
and load those into the Code Viewer widgets.} open return_type void
comment {Generate a header, source, strings, or design file and load the content into
the Code Viewer widgets. Source and header code are generated entirely in
memory (via ostringstream) and pushed directly into the text buffers — no
temporary files are written or read back for those tabs.} open return_type void
} {
code {if (!codeview_panel || !codeview_panel->visible())
return;
return;
if (!cv_source_filename) {
cv_source_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_source_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_source_filename, "codeview_tmp.cxx", FL_PATH_MAX);
if (!cv_design_filename) {
cv_design_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX);
}
if (cv_project->visible_r()) {
fld::io::write_file(Fluid.proj, cv_design_filename, false, true);
int top = cv_project->top_line();
cv_project->buffer()->loadfile(cv_design_filename);
cv_project->scroll(top, 0);
} else if (cv_strings->visible_r()) {
static const char *exts[] = { ".txt", ".po", ".msg" };
char fn[FL_PATH_MAX+1];
fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(fn, "strings", FL_PATH_MAX);
fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast<int>(Fluid.proj.i18n.type)]);
fld::io::write_strings(Fluid.proj, fn);
int top = cv_strings->top_line();
cv_strings->buffer()->loadfile(fn);
cv_strings->scroll(top, 0);
} else if (cv_source->visible_r() || cv_header->visible_r()) {
// Generate code into in-memory ostringstream buffers (no temp files).
fld::io::Code_Writer f(Fluid.proj);
std::string code_filename = Fluid.proj.codefile_path() + Fluid.proj.codefile_name();
std::string header_filename = Fluid.proj.headerfile_path() + Fluid.proj.headerfile_name();
if (Fluid.proj.proj_filename)
Fluid.proj.enter_project_dir();
// Setting code_view to true write code files directly into a string buffer
// and does not write over the code files on disk.
if (f.write_code(code_filename.c_str(), header_filename.c_str(), true))
{
// Push generated text directly into the editor buffers.
int pos = cv_source->top_line();
cv_source->buffer()->text(f.code_string().c_str());
cv_source->scroll(pos, 0);
pos = cv_header->top_line();
cv_header->buffer()->text(f.header_string().c_str());
cv_header->scroll(pos, 0);
// update the source code highlighting
update_codeview_position();
}
if (!cv_header_filename) {
cv_header_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_header_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_header_filename, "codeview_tmp.h", FL_PATH_MAX);
}
if (!cv_design_filename) {
cv_design_filename = (char*)malloc(FL_PATH_MAX);
fl_strlcpy(cv_design_filename, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(cv_design_filename, "codeview_tmp.fl", FL_PATH_MAX);
}
if (cv_project->visible_r()) {
fld::io::write_file(Fluid.proj, cv_design_filename, false, true);
int top = cv_project->top_line();
cv_project->buffer()->loadfile(cv_design_filename);
cv_project->scroll(top, 0);
} else if (cv_strings->visible_r()) {
static const char *exts[] = { ".txt", ".po", ".msg" };
char fn[FL_PATH_MAX+1];
fl_strlcpy(fn, Fluid.get_tmpdir().c_str(), FL_PATH_MAX);
fl_strlcat(fn, "strings", FL_PATH_MAX);
fl_filename_setext(fn, FL_PATH_MAX, exts[static_cast<int>(Fluid.proj.i18n.type)]);
fld::io::write_strings(Fluid.proj, fn);
int top = cv_strings->top_line();
cv_strings->buffer()->loadfile(fn);
cv_strings->scroll(top, 0);
} else if (cv_source->visible_r() || cv_header->visible_r()) {
std::string code_file_name_bak = Fluid.proj.code_file_name;
Fluid.proj.code_file_name = cv_source_filename;
std::string header_file_name_bak = Fluid.proj.header_file_name;
Fluid.proj.header_file_name = cv_header_filename;
// generate the code and load the files
fld::io::Code_Writer f(Fluid.proj);
// generate files
if (f.write_code(cv_source_filename, cv_header_filename, true))
{
// load file into source editor
int pos = cv_source->top_line();
cv_source->buffer()->loadfile(cv_source_filename);
cv_source->scroll(pos, 0);
// load file into header editor
pos = cv_header->top_line();
cv_header->buffer()->loadfile(cv_header_filename);
cv_header->scroll(pos, 0);
// update the source code highlighting
update_codeview_position();
}
Fluid.proj.code_file_name = code_file_name_bak;
Fluid.proj.header_file_name = header_file_name_bak;
}} {selected
if (Fluid.proj.proj_filename)
Fluid.proj.leave_project_dir();
}} {selected
}
}
+25
View File
@@ -0,0 +1,25 @@
# generated by Fast Light User Interface Designer (fluid) version 1.0500
Code View
Source
Header
Strings
Project
aA
Find:
<<
>>
Reveal
Refresh
Auto-Refresh
Auto-Position
prolog
Include statements in header or source code
static
static declarations in source code
code
widget code block including children
code 1
widget code block before children
code 2
widget code block after children
Close
+31 -15
View File
@@ -358,33 +358,49 @@ bool Mergeback::read_tag(const char *tag, Tag *prev_type, uint16_t *uid, uint32_
}
void Mergeback::print_tag(FILE *out, Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc) {
std::string tag = format_tag(prev_type, next_type, uid, crc);
fputs(tag.c_str(), out);
}
/**
Format a MergeBack tag as a string.
\param[in] prev_type Type of the previous block.
\param[in] next_type Type of the next block.
\param[in] uid Unique ID of the node.
\param[in] crc CRC32 of the previous block.
\return The formatted tag string.
*/
std::string Mergeback::format_tag(Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc) {
static const char *tag_lut[] = { "----------", "-- code --", " callback ", " callback " };
fputs("//fl ", out); // Distinct start of tag using utf8
static const char *lut[] = { "--", "-~", "~-", "~~", "-=", "=-", "~=", "=~" };
std::string result;
result += "//fl "; // Distinct start of tag using utf8
// Indicate that the text above can be edited
if (prev_type != Tag::GENERIC) fputs("", out);
if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) fputc('/', out);
if (prev_type != Tag::GENERIC) result += "";
if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) result += '/';
// Indicate that the text below can be edited
if (next_type != Tag::GENERIC) fputs("", out);
fputc(' ', out);
if (next_type != Tag::GENERIC) result += "";
result += ' ';
// Write the first 32 bit word as an encoded divider line
uint32_t pt = static_cast<uint32_t>(prev_type);
uint32_t nt = static_cast<uint32_t>(next_type);
uint32_t word = (0<<24) | (pt<<16) | (uid); // top 8 bit available for encoding type
print_trichar32(out, word);
for (int i=30; i>=0; i-=3) result += lut[(word>>i)&7];
// Write a string indicating the type of editable text
if ( next_type != Tag::GENERIC) {
fputs(tag_lut[nt], out);
result += tag_lut[nt];
} else if (prev_type != Tag::GENERIC) {
fputs(tag_lut[nt], out);
result += tag_lut[nt];
}
// Write the second 32 bit word as an encoded divider line
print_trichar32(out, crc);
// Repeat the intor pattern
fputc(' ', out);
if (prev_type != Tag::GENERIC) fputs("", out);
if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) fputc('/', out);
if (next_type != Tag::GENERIC) fputs("", out);
fputs(" fl//\n", out);
for (int i=30; i>=0; i-=3) result += lut[(crc>>i)&7];
// Repeat the intro pattern
result += ' ';
if (prev_type != Tag::GENERIC) result += "";
if (prev_type != Tag::GENERIC && next_type != Tag::GENERIC) result += '/';
if (next_type != Tag::GENERIC) result += "";
result += " fl//\n";
return result;
}
/** Analyze the code file and return findings in class member variables.
+1
View File
@@ -82,6 +82,7 @@ public:
int analyse();
int apply();
static void print_tag(FILE *out, Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc);
static std::string format_tag(Tag prev_type, Tag next_type, uint16_t uid, uint32_t crc);
};
extern int merge_back(const std::string &s, const std::string &p, int task);