summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--buffer.c84
-rw-r--r--main.c10
-rw-r--r--ted-internal.h21
-rw-r--r--ted.c42
5 files changed, 106 insertions, 52 deletions
diff --git a/.gitignore b/.gitignore
index 0f052e8..9e662e9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ TAGS
*.msi
*.zip
*.a
+*.asc
SDL2
*.deb
.vs
diff --git a/buffer.c b/buffer.c
index 5f34cba..fd16181 100644
--- a/buffer.c
+++ b/buffer.c
@@ -12,13 +12,6 @@
#elif _WIN32
#include <io.h>
#endif
-#if __linux__
-#include <sys/inotify.h>
-#define HAS_INOTIFY 1
-#define INOTIFY_MASK (IN_ATTRIB | IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF)
-#else
-#define HAS_INOTIFY 0
-#endif
/// A single line in a buffer
typedef struct Line Line;
@@ -91,6 +84,8 @@ struct TextBuffer {
bool indent_with_spaces;
/// true if user has manually specified indentation
bool manual_indentation;
+ /// set to true in ted.c when buffer has been modified according to inotify
+ bool inotify_modified;
/// tab size
///
/// this is either set according to the user's settings or according to the autodetected indentation
@@ -140,7 +135,7 @@ struct TextBuffer {
/// dynamic array of redo history
BufferEdit *redo_history;
#if HAS_INOTIFY
- int inotify_fd, inotify_watch;
+ int inotify_watch;
#endif
};
@@ -242,6 +237,7 @@ double buffer_last_write_time(TextBuffer *buffer) {
void buffer_ignore_changes_on_disk(TextBuffer *buffer) {
buffer->last_write_time = timespec_to_seconds(time_last_modified(buffer->path));
+ buffer->inotify_modified = false;
// no matter what, buffer_unsaved_changes should return true
buffer->undo_history_write_pos = U32_MAX;
}
@@ -385,7 +381,6 @@ static void buffer_set_up(Ted *ted, TextBuffer *buffer) {
buffer->indent_with_spaces = settings->indent_with_spaces;
buffer->tab_width = settings->tab_width;
#if HAS_INOTIFY
- buffer->inotify_fd = -1;
buffer->inotify_watch = -1;
#endif
}
@@ -1040,9 +1035,8 @@ static void buffer_free_inner(TextBuffer *buffer) {
arr_free(buffer->redo_history);
settings_free(&buffer->settings);
#if HAS_INOTIFY
- if (buffer->inotify_fd != -1) {
- close(buffer->inotify_fd);
- buffer->inotify_fd = -1;
+ if (buffer->inotify_watch != -1) {
+ inotify_rm_watch(ted->inotify_fd, buffer->inotify_watch);
buffer->inotify_watch = -1;
}
#endif
@@ -3071,20 +3065,17 @@ Status buffer_load_file(TextBuffer *buffer, const char *path) {
// it's important we do this first, since someone might write to the file while we're reading it,
// and we want to detect that in buffer_externally_changed
double modified_time = timespec_to_seconds(time_last_modified(path));
+ Ted *ted = buffer->ted;
#if HAS_INOTIFY
- int inotify_fd = reload ? buffer->inotify_fd : -1,
- inotify_watch = reload ? buffer->inotify_watch : -1;
- if (inotify_fd == -1) {
- inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
- if (inotify_fd == -1) {
- perror("inotify_init1");
- }
- } else if (inotify_watch != -1) {
- inotify_rm_watch(inotify_fd, inotify_watch);
+ if (buffer->inotify_watch != -1) {
+ // important we remove it and recreate it to handle edge cases like
+ // "file was renamed and new file was created with the same name"
+ inotify_rm_watch(ted->inotify_fd, buffer->inotify_watch);
+ buffer->inotify_watch = -1;
}
- int inotify_err = 0;
- if (inotify_fd != -1) {
- inotify_watch = inotify_add_watch(inotify_fd, path, INOTIFY_MASK);
+ int inotify_err = 0, inotify_watch = -1;
+ if (ted->inotify_fd != -1) {
+ inotify_watch = inotify_add_watch(ted->inotify_fd, path, INOTIFY_MASK);
if (inotify_watch == -1) inotify_err = errno;
}
#endif
@@ -3103,7 +3094,7 @@ Status buffer_load_file(TextBuffer *buffer, const char *path) {
success = false;
}
size_t file_size = 0;
- const Settings *default_settings = ted_default_settings(buffer->ted);
+ const Settings *default_settings = ted_default_settings(ted);
u32 max_file_size_editable = default_settings->max_file_size;
u32 max_file_size_view_only = default_settings->max_file_size_view_only;
if (success) {
@@ -3224,13 +3215,13 @@ Status buffer_load_file(TextBuffer *buffer, const char *path) {
// do this regardless of whether we were successful so we don't spam errors
// if the file is deleted, for example.
buffer->last_write_time = modified_time;
+ buffer->inotify_modified = false;
if (success) {
buffer->frame_earliest_line_modified = 0;
buffer->frame_latest_line_modified = nlines - 1;
buffer->undo_history_write_pos = arr_len(buffer->undo_history);
#if HAS_INOTIFY
buffer->inotify_watch = inotify_watch;
- buffer->inotify_fd = inotify_fd;
#endif
if (!(fs_path_permission(path) & FS_PERMISSION_WRITE)) {
// can't write to this file; make the buffer view only.
@@ -3244,9 +3235,8 @@ Status buffer_load_file(TextBuffer *buffer, const char *path) {
}
if (!success) {
#if HAS_INOTIFY
- if (inotify_fd != -1) {
- close(inotify_fd);
- buffer->inotify_watch = buffer->inotify_fd = -1;
+ if (inotify_watch != -1) {
+ inotify_rm_watch(ted->inotify_fd, inotify_watch);
}
#endif
// something went wrong; we need to free all the memory we used
@@ -3272,29 +3262,11 @@ bool buffer_externally_changed(TextBuffer *buffer) {
if (!buffer_is_named_file(buffer))
return false;
double last_modified = timespec_to_seconds(time_last_modified(buffer->path));
- bool modified = last_modified != buffer->last_write_time;
- #if HAS_INOTIFY
// for whatever reason, last modified time seems to be very slightly unreliable on Linux
// (occasionally we read a file and its contents later change shortly thereafter
// w/o the modified time changing)
// so we use inotify as a "backup"
- if (buffer->inotify_watch != -1) {
- char events[16384 * sizeof(struct inotify_event)];
- ssize_t bytes = read(buffer->inotify_fd, events, sizeof events);
- for (int i = 0; i + (int)sizeof(struct inotify_event) <= bytes; i += (int)sizeof(struct inotify_event)) {
- struct inotify_event event;
- memcpy(&event, &events[i * (int)sizeof event], sizeof event);
- if (event.mask & INOTIFY_MASK)
- modified = true;
- i += (int)event.len; // should never happen in theory...
- }
- // ideally we would sit in a loop here but then we'd hang if someone is constantly
- // changing the file. probably no good way of dealing with that though.
- // for what it's worth, 16384 is the default max inotify queue size, so we should always
- // read all the events with that one read call.
- }
- #endif
- return modified;
+ return last_modified != buffer->last_write_time || buffer->inotify_modified;
}
void buffer_new_file(TextBuffer *buffer, const char *path) {
@@ -3317,10 +3289,11 @@ void buffer_new_file(TextBuffer *buffer, const char *path) {
static bool buffer_write_to_file(TextBuffer *buffer, const char *path) {
const Settings *settings = buffer_settings(buffer);
+ Ted *ted = buffer->ted;
#if HAS_INOTIFY
if (buffer->inotify_watch != -1) {
// temporarily stop inotify events while we're writing to the buffer
- inotify_rm_watch(buffer->inotify_fd, buffer->inotify_watch);
+ inotify_rm_watch(ted->inotify_fd, buffer->inotify_watch);
buffer->inotify_watch = -1;
}
#endif
@@ -3404,9 +3377,9 @@ static bool buffer_write_to_file(TextBuffer *buffer, const char *path) {
success = false;
}
#if HAS_INOTIFY
- if (buffer->inotify_fd != -1) {
+ if (ted->inotify_fd != -1) {
// re-enable inotify
- buffer->inotify_watch = inotify_add_watch(buffer->inotify_fd, path, INOTIFY_MASK);
+ buffer->inotify_watch = inotify_add_watch(ted->inotify_fd, path, INOTIFY_MASK);
}
#endif
@@ -3456,6 +3429,7 @@ bool buffer_save(TextBuffer *buffer) {
if (success && *backup_path)
remove(backup_path);
buffer->last_write_time = timespec_to_seconds(time_last_modified(buffer->path));
+ buffer->inotify_modified = false;
if (success) {
buffer->undo_history_write_pos = arr_len(buffer->undo_history);
const char *filename = path_filename(buffer->path);
@@ -4268,3 +4242,11 @@ void buffer_set_manual_tab_width(TextBuffer *buffer, u8 n) {
buffer->tab_width = n;
buffer->manual_indentation = true;
}
+
+int buffer_inotify_watch(TextBuffer *buffer) {
+ return buffer->inotify_watch;
+}
+
+void buffer_set_inotify_modified(TextBuffer *buffer) {
+ buffer->inotify_modified = true;
+}
diff --git a/main.c b/main.c
index 3ca8029..b8e8528 100644
--- a/main.c
+++ b/main.c
@@ -383,6 +383,7 @@ int main(int argc, char **argv) {
if (!ted) {
die("Not enough memory available to run ted.");
}
+ ted->inotify_fd = -1;
ted->last_save_time = -1e50;
ted->pid = process_get_id();
ted_update_time(ted);
@@ -604,6 +605,12 @@ int main(int argc, char **argv) {
PROFILE_TIME(fonts_end)
PROFILE_TIME(create_start)
+ #if HAS_INOTIFY
+ ted->inotify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
+ if (ted->inotify_fd == -1) {
+ perror("inotify_init1");
+ }
+ #endif
{
TextBuffer *lbuffer = ted->line_buffer = line_buffer_new(ted);
if (!lbuffer || buffer_has_error(lbuffer))
@@ -901,6 +908,9 @@ int main(int argc, char **argv) {
// check if active buffer should be reloaded
{
TextBuffer *active_buffer = ted->active_buffer;
+ #if HAS_INOTIFY
+ ted_check_inotify(ted);
+ #endif
if (active_buffer && buffer_externally_changed(active_buffer)) {
if (buffer_settings(active_buffer)->auto_reload)
buffer_reload(active_buffer);
diff --git a/ted-internal.h b/ted-internal.h
index 466c6c3..a18228a 100644
--- a/ted-internal.h
+++ b/ted-internal.h
@@ -13,6 +13,13 @@
#include "ds.h"
#include "sdl-inc.h"
#include "lib/glcorearb.h"
+#if __linux__
+#include <sys/inotify.h>
+#define HAS_INOTIFY 1
+#define INOTIFY_MASK (IN_ATTRIB | IN_CLOSE_WRITE | IN_DELETE_SELF | IN_MODIFY | IN_MOVE_SELF)
+#else
+#define HAS_INOTIFY 0
+#endif
#if PROFILE
#define PROFILE_TIME(var) double var = time_get_seconds();
@@ -436,7 +443,9 @@ struct Ted {
MessageType message_type;
MessageType message_shown_type;
char message_shown[512];
-
+
+ // file descriptor used to access inotify API on Linux
+ int inotify_fd;
u64 edit_notify_id;
EditNotifyInfo *edit_notifys;
@@ -492,6 +501,14 @@ void buffer_center_cursor_next_frame(TextBuffer *buffer);
void buffer_check_valid(TextBuffer *buffer);
void buffer_publish_diagnostics(TextBuffer *buffer, const LSPRequest *request, LSPDiagnostic *diagnostics);
void buffer_print_undo_history(TextBuffer *buffer);
+#if HAS_INOTIFY
+/// get watch descriptor for this buffer for inotify tracking
+///
+/// returns -1 if there is none.
+int buffer_inotify_watch(TextBuffer *buffer);
+/// inform buffer that its watch on the Ted's inotify was modified
+void buffer_set_inotify_modified(TextBuffer *buffer);
+#endif
// === build.c ===
void build_frame(Ted *ted, float x1, float y1, float x2, float y2);
@@ -753,5 +770,7 @@ void ted_load_fonts(Ted *ted);
void ted_free_fonts(Ted *ted);
/// process textDocument/publishDiagnostics request
void ted_process_publish_diagnostics(Ted *ted, LSP *lsp, LSPRequest *request);
+/// check inotify fd for events
+void ted_check_inotify(Ted *ted);
#endif // TED_INTERNAL_H_
diff --git a/ted.c b/ted.c
index 7cddd68..3fcf266 100644
--- a/ted.c
+++ b/ted.c
@@ -3,8 +3,11 @@
#include "ted-internal.h"
#if _WIN32
#include <SDL_syswm.h>
+#elif __unix__
+ #include <unistd.h>
#endif
+
struct LoadedFont {
char *path;
Font *font;
@@ -978,3 +981,42 @@ void ted_test(Ted *ted) {
#undef run_test
printf("all good as far as i know :3\n");
}
+
+#if HAS_INOTIFY
+void ted_check_inotify(Ted *ted) {
+ // see buffer_externally_changed definition for why this exists
+ if (ted->inotify_fd == -1) return;
+ int *watches_modified = NULL;
+ char events[16384 * sizeof(struct inotify_event)];
+ ssize_t bytes = read(ted->inotify_fd, events, sizeof events);
+ for (int i = 0; i + (int)sizeof(struct inotify_event) <= bytes; i += (int)sizeof(struct inotify_event)) {
+ struct inotify_event event;
+ memcpy(&event, &events[i * (int)sizeof event], sizeof event);
+ if (event.mask & INOTIFY_MASK) {
+ bool new = true;
+ arr_foreach_ptr(watches_modified, int, w) {
+ if (*w == event.wd) {
+ new = false;
+ break;
+ }
+ }
+ if (new) arr_add(watches_modified, event.wd);
+ }
+ i += (int)event.len; // should always be 0 in theory...
+ }
+ // ideally we'd read more events in a loop here but then we'd hang if someone is constantly
+ // changing the file. probably no good way of dealing with that though.
+ // for what it's worth, 16384 is the default max inotify queue size, so we should always
+ // read all the events with that one read call.
+ arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) {
+ int watch = buffer_inotify_watch(*pbuffer);
+ arr_foreach_ptr(watches_modified, int, w) {
+ if (*w == watch) {
+ buffer_set_inotify_modified(*pbuffer);
+ break;
+ }
+ }
+ }
+ arr_free(watches_modified);
+}
+#endif