mirror of
https://github.com/fltk/fltk.git
synced 2026-05-10 05:27:55 +08:00
Fluid: write code files only when the actually change (STR 1859) (#1406)
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:
+1
-1
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user