summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buffer.c375
-rw-r--r--config.c1
-rw-r--r--main.c10
-rw-r--r--node.c46
-rw-r--r--ted.c1
-rw-r--r--ted.cfg2
-rw-r--r--ted.h4
7 files changed, 242 insertions, 197 deletions
diff --git a/buffer.c b/buffer.c
index d42d53a..ec2b748 100644
--- a/buffer.c
+++ b/buffer.c
@@ -570,179 +570,6 @@ void buffer_clear(TextBuffer *buffer) {
memcpy(buffer->error, error, sizeof error);
}
-// if an error occurs, buffer is left untouched (except for the error field) and the function returns false.
-Status buffer_load_file(TextBuffer *buffer, char const *filename) {
- FILE *fp = fopen(filename, "rb");
- bool success = true;
- if (fp) {
- fseek(fp, 0, SEEK_END);
- long file_pos = ftell(fp);
- size_t file_size = (size_t)file_pos;
- fseek(fp, 0, SEEK_SET);
- if (file_pos == -1 || file_pos == LONG_MAX) {
- buffer_seterr(buffer, "Couldn't get file position. There is something wrong with the file '%s'.", filename);
- success = false;
- } else if (file_size > 10L<<20) {
- buffer_seterr(buffer, "File too big (size: %zu).", file_size);
- success = false;
- } else {
- u8 *file_contents = buffer_calloc(buffer, 1, file_size);
- if (file_contents) {
- u32 lines_capacity = 4;
- Line *lines = buffer_calloc(buffer, lines_capacity, sizeof *buffer->lines); // initial lines
- if (lines) {
- u32 nlines = 1;
- size_t bytes_read = fread(file_contents, 1, file_size, fp);
- if (bytes_read == file_size) {
- char32_t c = 0;
- for (u8 *p = file_contents, *end = p + file_size; p != end; ) {
- if (*p == '\r' && p != end-1 && p[1] == '\n') {
- // CRLF line endings
- p += 2;
- c = '\n';
- } else {
- size_t n = unicode_utf8_to_utf32(&c, (char *)p, (size_t)(end - p));
- if (n == 0) {
- // null character
- c = 0;
- ++p;
- } else if (n == (size_t)(-1)) {
- // invalid UTF-8
- success = false;
- buffer_seterr(buffer, "Invalid UTF-8 (position: %td).", p - file_contents);
- break;
- } else {
- p += n;
- }
- }
- if (c == '\n') {
- if (buffer_lines_set_min_capacity(buffer, &lines, &lines_capacity, nlines + 1))
- ++nlines;
- } else {
- u32 line_idx = nlines - 1;
- Line *line = &lines[line_idx];
- buffer_line_append_char(buffer, line, c);
- }
- }
- if (success) {
- char *filename_copy = buffer_strdup(buffer, filename);
- if (!filename_copy) success = false;
- if (success) {
- // everything is good
- buffer_clear(buffer);
- buffer->lines = lines;
- buffer->nlines = nlines;
- buffer->frame_earliest_line_modified = 0;
- buffer->frame_latest_line_modified = nlines - 1;
- buffer->lines_capacity = lines_capacity;
- buffer->filename = filename_copy;
- }
- }
- } else {
- success = false;
- }
- if (!success) {
- // something went wrong; we need to free all the memory we used
- for (u32 i = 0; i < nlines; ++i)
- buffer_line_free(&lines[i]);
- free(lines);
- }
- }
- free(file_contents);
- }
- if (ferror(fp)) {
- buffer_seterr(buffer, "Error reading from file.");
- success = false;
- }
- }
- if (fclose(fp) != 0) {
- buffer_seterr(buffer, "Error closing file.");
- success = false;
- }
- } else {
- buffer_seterr(buffer, "Couldn't open file %s: %s.", filename, strerror(errno));
- success = false;
- }
- return success;
-}
-
-void buffer_new_file(TextBuffer *buffer, char const *filename) {
- buffer_clear(buffer);
-
- buffer->filename = buffer_strdup(buffer, filename);
- buffer->lines_capacity = 4;
- buffer->lines = buffer_calloc(buffer, buffer->lines_capacity, sizeof *buffer->lines);
- buffer->nlines = 1;
-}
-
-// Save the buffer to its current filename. This will rewrite the entire file, regardless of
-// whether there are any unsaved changes.
-bool buffer_save(TextBuffer *buffer) {
- Settings const *settings = buffer_settings(buffer);
- if (!buffer->is_line_buffer && buffer->filename) {
- FILE *out = fopen(buffer->filename, "wb");
- if (out) {
- for (Line *line = buffer->lines, *end = line + buffer->nlines; line != end; ++line) {
- for (char32_t *p = line->str, *p_end = p + line->len; p != p_end; ++p) {
- char utf8[4] = {0};
- size_t bytes = unicode_utf32_to_utf8(utf8, *p);
- if (bytes != (size_t)-1) {
- if (fwrite(utf8, 1, bytes, out) != bytes) {
- buffer_seterr(buffer, "Couldn't write to %s.", buffer->filename);
- }
- }
- }
-
- if (line != end-1) {
- putc('\n', out);
- } else {
- if (settings->auto_add_newline) {
- if (line->len) {
- // if the last line isn't empty, add a newline.
- putc('\n', out);
- }
- }
- }
- }
- if (ferror(out)) {
- if (!buffer_haserr(buffer))
- buffer_seterr(buffer, "Couldn't write to %s.", buffer->filename);
- }
- if (fclose(out) != 0) {
- if (!buffer_haserr(buffer))
- buffer_seterr(buffer, "Couldn't close file %s.", buffer->filename);
- }
- bool success = !buffer_haserr(buffer);
- if (success) {
- buffer->modified = false;
- }
- return success;
- } else {
- buffer_seterr(buffer, "Couldn't open file %s for writing: %s.", buffer->filename, strerror(errno));
- return false;
- }
- } else {
- // user tried to save line buffer. whatever
- return true;
- }
-}
-
-// save, but with a different file name
-bool buffer_save_as(TextBuffer *buffer, char const *new_filename) {
- char *prev_filename = buffer->filename;
- if ((buffer->filename = buffer_strdup(buffer, new_filename))) {
- if (buffer_save(buffer)) {
- free(prev_filename);
- return true;
- } else {
- free(buffer->filename);
- buffer->filename = prev_filename;
- return false;
- }
- } else {
- return false;
- }
-}
// print the contents of a buffer to stdout
static void buffer_print(TextBuffer const *buffer) {
@@ -1859,6 +1686,208 @@ void buffer_paste(TextBuffer *buffer) {
}
}
+
+// if an error occurs, buffer is left untouched (except for the error field) and the function returns false.
+Status buffer_load_file(TextBuffer *buffer, char const *filename) {
+ FILE *fp = fopen(filename, "rb");
+ bool success = true;
+ if (fp) {
+ fseek(fp, 0, SEEK_END);
+ long file_pos = ftell(fp);
+ size_t file_size = (size_t)file_pos;
+ fseek(fp, 0, SEEK_SET);
+ if (file_pos == -1 || file_pos == LONG_MAX) {
+ buffer_seterr(buffer, "Couldn't get file position. There is something wrong with the file '%s'.", filename);
+ success = false;
+ } else if (file_size > 10L<<20) {
+ buffer_seterr(buffer, "File too big (size: %zu).", file_size);
+ success = false;
+ } else {
+ u8 *file_contents = buffer_calloc(buffer, 1, file_size);
+ if (file_contents) {
+ u32 lines_capacity = 4;
+ Line *lines = buffer_calloc(buffer, lines_capacity, sizeof *buffer->lines); // initial lines
+ if (lines) {
+ u32 nlines = 1;
+ size_t bytes_read = fread(file_contents, 1, file_size, fp);
+ if (bytes_read == file_size) {
+ char32_t c = 0;
+ for (u8 *p = file_contents, *end = p + file_size; p != end; ) {
+ if (*p == '\r' && p != end-1 && p[1] == '\n') {
+ // CRLF line endings
+ p += 2;
+ c = '\n';
+ } else {
+ size_t n = unicode_utf8_to_utf32(&c, (char *)p, (size_t)(end - p));
+ if (n == 0) {
+ // null character
+ c = 0;
+ ++p;
+ } else if (n == (size_t)(-1)) {
+ // invalid UTF-8
+ success = false;
+ buffer_seterr(buffer, "Invalid UTF-8 (position: %td).", p - file_contents);
+ break;
+ } else {
+ p += n;
+ }
+ }
+ if (c == '\n') {
+ if (buffer_lines_set_min_capacity(buffer, &lines, &lines_capacity, nlines + 1))
+ ++nlines;
+ } else {
+ u32 line_idx = nlines - 1;
+ Line *line = &lines[line_idx];
+ buffer_line_append_char(buffer, line, c);
+ }
+ }
+ if (success) {
+ char *filename_copy = buffer_strdup(buffer, filename);
+ if (!filename_copy) success = false;
+ if (success) {
+ // everything is good
+ buffer_clear(buffer);
+ buffer->lines = lines;
+ buffer->nlines = nlines;
+ buffer->frame_earliest_line_modified = 0;
+ buffer->frame_latest_line_modified = nlines - 1;
+ buffer->lines_capacity = lines_capacity;
+ buffer->filename = filename_copy;
+ }
+ }
+ } else {
+ success = false;
+ }
+ if (!success) {
+ // something went wrong; we need to free all the memory we used
+ for (u32 i = 0; i < nlines; ++i)
+ buffer_line_free(&lines[i]);
+ free(lines);
+ }
+ }
+ free(file_contents);
+ }
+ if (ferror(fp)) {
+ buffer_seterr(buffer, "Error reading from file.");
+ success = false;
+ }
+ }
+ if (fclose(fp) != 0) {
+ buffer_seterr(buffer, "Error closing file.");
+ success = false;
+ }
+ } else {
+ buffer_seterr(buffer, "Couldn't open file %s: %s.", filename, strerror(errno));
+ success = false;
+ }
+ return success;
+}
+
+// Reloads the file loaded in the buffer.
+// Note that this clears undo history, etc.
+void buffer_reload(TextBuffer *buffer) {
+ if (buffer->filename && !buffer_is_untitled(buffer)) {
+ BufferPos cursor_pos = buffer->cursor_pos;
+ buffer_load_file(buffer, buffer->filename);
+ buffer->cursor_pos = cursor_pos;
+ buffer_validate_cursor(buffer);
+ }
+}
+
+// has this buffer been changed by another program since last save?
+bool buffer_externally_changed(TextBuffer *buffer) {
+ if (!buffer->filename || buffer_is_untitled(buffer))
+ return false;
+
+ if (!timespec_eq(buffer->last_write_time, time_last_modified(buffer->filename))) {
+ // the write time has been updated, but has the file actually been changed?
+ // @TODO
+ }
+ return false;
+}
+
+void buffer_new_file(TextBuffer *buffer, char const *filename) {
+ buffer_clear(buffer);
+
+ buffer->filename = buffer_strdup(buffer, filename);
+ buffer->lines_capacity = 4;
+ buffer->lines = buffer_calloc(buffer, buffer->lines_capacity, sizeof *buffer->lines);
+ buffer->nlines = 1;
+}
+
+// Save the buffer to its current filename. This will rewrite the entire file, regardless of
+// whether there are any unsaved changes.
+bool buffer_save(TextBuffer *buffer) {
+ Settings const *settings = buffer_settings(buffer);
+ if (!buffer->is_line_buffer && buffer->filename) {
+ FILE *out = fopen(buffer->filename, "wb");
+ if (out) {
+ for (u32 i = 0; i < buffer->nlines; ++i) {
+ Line *line = &buffer->lines[i];
+ for (char32_t *p = line->str, *p_end = p + line->len; p != p_end; ++p) {
+ char utf8[4] = {0};
+ size_t bytes = unicode_utf32_to_utf8(utf8, *p);
+ if (bytes != (size_t)-1) {
+ if (fwrite(utf8, 1, bytes, out) != bytes) {
+ buffer_seterr(buffer, "Couldn't write to %s.", buffer->filename);
+ }
+ }
+ }
+
+ if (i != buffer->nlines-1) {
+ putc('\n', out);
+ } else {
+ if (settings->auto_add_newline) {
+ if (line->len) {
+ // if the last line isn't empty, add a newline to the end of the file
+ char32_t c = '\n';
+ String32 s = {&c, 1};
+ buffer_insert_text_at_pos(buffer, buffer_end_of_file(buffer), s);
+ }
+ }
+ }
+ }
+ if (ferror(out)) {
+ if (!buffer_haserr(buffer))
+ buffer_seterr(buffer, "Couldn't write to %s.", buffer->filename);
+ }
+ if (fclose(out) != 0) {
+ if (!buffer_haserr(buffer))
+ buffer_seterr(buffer, "Couldn't close file %s.", buffer->filename);
+ }
+ bool success = !buffer_haserr(buffer);
+ if (success) {
+ buffer->modified = false;
+ }
+ buffer->last_write_time = time_last_modified(buffer->filename);
+ return success;
+ } else {
+ buffer_seterr(buffer, "Couldn't open file %s for writing: %s.", buffer->filename, strerror(errno));
+ return false;
+ }
+ } else {
+ // user tried to save line buffer. whatever
+ return true;
+ }
+}
+
+// save, but with a different file name
+bool buffer_save_as(TextBuffer *buffer, char const *new_filename) {
+ char *prev_filename = buffer->filename;
+ if ((buffer->filename = buffer_strdup(buffer, new_filename))) {
+ if (buffer_save(buffer)) {
+ free(prev_filename);
+ return true;
+ } else {
+ free(buffer->filename);
+ buffer->filename = prev_filename;
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
// for debugging
#if DEBUG
static void buffer_pos_check_valid(TextBuffer *buffer, BufferPos p) {
diff --git a/config.c b/config.c
index b389867..9589749 100644
--- a/config.c
+++ b/config.c
@@ -174,6 +174,7 @@ void config_read(Ted *ted, char const *filename) {
OptionBool const options_bool[] = {
{"auto-indent", &settings->auto_indent},
{"auto-add-newline", &settings->auto_add_newline},
+ {"auto-reload", &settings->auto_reload},
{"syntax-highlighting", &settings->syntax_highlighting},
{"line-numbers", &settings->line_numbers},
};
diff --git a/main.c b/main.c
index 96820b6..e4445b8 100644
--- a/main.c
+++ b/main.c
@@ -39,8 +39,8 @@ no_warn_end
#include "command.h"
#include "colors.h"
-#include "ted.h"
#include "time.c"
+#include "ted.h"
#include "string32.c"
#include "arr.c"
#include "syntax.c"
@@ -462,6 +462,14 @@ int main(int argc, char **argv) {
}
}
}
+
+ // check if active buffer should be reloaded
+ {
+ TextBuffer *active_buffer = ted->active_buffer;
+ if (active_buffer && buffer_externally_changed(active_buffer)) {
+ buffer_reload(active_buffer);
+ }
+ }
u32 key_modifier = (u32)ctrl_down << KEY_MODIFIER_CTRL_BIT
diff --git a/node.c b/node.c
index 9a37d4a..0713977 100644
--- a/node.c
+++ b/node.c
@@ -94,31 +94,33 @@ static void node_frame(Ted *ted, Node *node, Rect r) {
{ // tab bar
u16 ntabs = (u16)arr_len(node->tabs);
float tab_width = r.size.x / ntabs;
- for (u16 c = 0; c < ted->nmouse_clicks[SDL_BUTTON_LEFT]; ++c) {
- v2 click = ted->mouse_clicks[SDL_BUTTON_LEFT][c];
- if (rect_contains_point(tab_bar_rect, click)) {
- u16 tab_index = (u16)((click.x - r.pos.x) / tab_width);
- node_switch_to_tab(ted, node, tab_index);
+ if (!ted->menu) {
+ for (u16 c = 0; c < ted->nmouse_clicks[SDL_BUTTON_LEFT]; ++c) {
+ v2 click = ted->mouse_clicks[SDL_BUTTON_LEFT][c];
+ if (rect_contains_point(tab_bar_rect, click)) {
+ u16 tab_index = (u16)((click.x - r.pos.x) / tab_width);
+ node_switch_to_tab(ted, node, tab_index);
+ }
}
- }
- for (u16 c = 0; c < ted->nmouse_clicks[SDL_BUTTON_MIDDLE]; ++c) {
- v2 click = ted->mouse_clicks[SDL_BUTTON_MIDDLE][c];
- if (rect_contains_point(tab_bar_rect, click)) {
- u16 tab_index = (u16)((click.x - r.pos.x) / tab_width);
- u16 buffer_idx = node->tabs[tab_index];
- TextBuffer *buffer = &ted->buffers[buffer_idx];
- // close that tab
- if (buffer_unsaved_changes(buffer)) {
- // make sure unsaved changes dialog is opened
- ted_switch_to_buffer(ted, buffer_idx);
- command_execute(ted, CMD_TAB_CLOSE, 1);
- } else {
- if (!node_tab_close(ted, node, tab_index)) {
- return; // node closed
+ for (u16 c = 0; c < ted->nmouse_clicks[SDL_BUTTON_MIDDLE]; ++c) {
+ v2 click = ted->mouse_clicks[SDL_BUTTON_MIDDLE][c];
+ if (rect_contains_point(tab_bar_rect, click)) {
+ u16 tab_index = (u16)((click.x - r.pos.x) / tab_width);
+ u16 buffer_idx = node->tabs[tab_index];
+ TextBuffer *buffer = &ted->buffers[buffer_idx];
+ // close that tab
+ if (buffer_unsaved_changes(buffer)) {
+ // make sure unsaved changes dialog is opened
+ ted_switch_to_buffer(ted, buffer_idx);
+ command_execute(ted, CMD_TAB_CLOSE, 1);
+ } else {
+ if (!node_tab_close(ted, node, tab_index)) {
+ return; // node closed
+ }
}
+ ntabs = (u16)arr_len(node->tabs);
+ tab_width = r.size.x / ntabs;
}
- ntabs = (u16)arr_len(node->tabs);
- tab_width = r.size.x / ntabs;
}
}
diff --git a/ted.c b/ted.c
index 4b80a97..a1cbed5 100644
--- a/ted.c
+++ b/ted.c
@@ -193,6 +193,7 @@ static bool ted_open_file(Ted *ted, char const *filename) {
for (u16 i = 0; i < TED_MAX_BUFFERS; ++i) {
if (buffers_used[i]) {
if (streq(filename, buffer_get_filename(&buffers[i]))) {
+ buffer_reload(&buffers[i]); // make sure buffer is up to date with the file
ted_switch_to_buffer(ted, i);
return true;
}
diff --git a/ted.cfg b/ted.cfg
index 96f00e2..339274e 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -21,6 +21,8 @@ auto-indent = on
auto-add-newline = on
syntax-highlighting = on
line-numbers = on
+# Reload files when changed?
+auto-reload = off
[keyboard]
# motion and selection
diff --git a/ted.h b/ted.h
index b0e6f6a..05283f7 100644
--- a/ted.h
+++ b/ted.h
@@ -71,6 +71,7 @@ typedef struct {
bool auto_add_newline;
bool syntax_highlighting;
bool line_numbers;
+ bool auto_reload;
u8 tab_width;
u8 cursor_width;
u8 undo_save_time;
@@ -124,6 +125,7 @@ typedef struct {
char *filename; // NULL if this buffer doesn't correspond to a file (e.g. line buffers)
struct Ted *ted; // we keep a back-pointer to the ted instance so we don't have to pass it in to every buffer function
double scroll_x, scroll_y; // number of characters scrolled in the x/y direction
+ struct timespec last_write_time; // last write time to filename.
BufferPos cursor_pos;
BufferPos selection_pos; // if selection is true, the text between selection_pos and cursor_pos is selected.
bool is_line_buffer; // "line buffers" are buffers which can only have one line of text (used for inputs)
@@ -151,7 +153,7 @@ ENUM_U16 {
MENU_NONE,
MENU_OPEN,
MENU_SAVE_AS,
- MENU_WARN_UNSAVED // warn about unsaved changes
+ MENU_WARN_UNSAVED, // warn about unsaved changes
} ENUM_U16_END(Menu);
// file entries for file selectors