From e1bd076f5e64010578b904ad4bbb25b462517ef3 Mon Sep 17 00:00:00 2001 From: Frank LENORMAND Date: Sat, 23 Nov 2019 15:55:46 +0100 Subject: [PATCH] src: Reload buffers when their contents' hash changes Instead of triggering a reload event when the timestamp of a buffer's underlying file changes, do so when its contents are actually modified. --- src/buffer.cc | 16 ++++++++-------- src/buffer.hh | 9 +++++---- src/client.cc | 15 +++++++++++---- src/file.cc | 7 +++++++ src/file.hh | 8 ++++++++ 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/src/buffer.cc b/src/buffer.cc index 6088c8a8..4e966a1a 100644 --- a/src/buffer.cc +++ b/src/buffer.cc @@ -78,7 +78,7 @@ Buffer::Buffer(String name, Flags flags, StringView data, m_history{{HistoryId::Invalid}}, m_history_id{HistoryId::First}, m_last_save_history_id{HistoryId::First}, - m_fs_timestamp{fs_timestamp.tv_sec, fs_timestamp.tv_nsec} + m_fs_status{fs_timestamp, data.length(), hash_value(data)} { ParsedLines parsed_lines = parse_lines(data); @@ -122,7 +122,7 @@ void Buffer::on_registered() run_hook_in_own_context(Hook::BufNewFile, m_name); else { - kak_assert(m_fs_timestamp != InvalidTime); + kak_assert(m_fs_status.timestamp != InvalidTime); run_hook_in_own_context(Hook::BufOpenFile, m_name); } } @@ -305,7 +305,7 @@ void Buffer::reload(StringView data, timespec fs_timestamp) apply_options(options(), parsed_lines); m_last_save_history_id = m_history_id; - m_fs_timestamp = fs_timestamp; + m_fs_status = {fs_timestamp, data.length(), hash_value(data)}; } void Buffer::commit_undo_group() @@ -607,7 +607,7 @@ void Buffer::notify_saved() m_flags &= ~Flags::New; m_last_save_history_id = m_history_id; - m_fs_timestamp = get_fs_timestamp(m_name); + m_fs_status.timestamp = get_fs_timestamp(m_name); } BufferCoord Buffer::advance(BufferCoord coord, ByteCount count) const @@ -663,16 +663,16 @@ BufferCoord Buffer::char_prev(BufferCoord coord) const return { coord.line, column }; } -timespec Buffer::fs_timestamp() const +void Buffer::set_fs_status(FsStatus status) { kak_assert(m_flags & Flags::File); - return m_fs_timestamp; + m_fs_status = std::move(status); } -void Buffer::set_fs_timestamp(timespec ts) +const FsStatus& Buffer::fs_status() const { kak_assert(m_flags & Flags::File); - m_fs_timestamp = ts; + return m_fs_status; } void Buffer::on_option_changed(const Option& option) diff --git a/src/buffer.hh b/src/buffer.hh index a586e828..e05ca6d1 100644 --- a/src/buffer.hh +++ b/src/buffer.hh @@ -5,6 +5,7 @@ #include "coord.hh" #include "constexpr_utils.hh" #include "enum.hh" +#include "file.hh" #include "optional.hh" #include "safe_ptr.hh" #include "scope.hh" @@ -141,9 +142,9 @@ public: BufferCoord erase(BufferCoord begin, BufferCoord end); BufferCoord replace(BufferCoord begin, BufferCoord end, StringView content); - size_t timestamp() const; - timespec fs_timestamp() const; - void set_fs_timestamp(timespec ts); + size_t timestamp() const; + void set_fs_status(FsStatus); + const FsStatus& fs_status() const; void commit_undo_group(); bool undo(size_t count = 1); @@ -284,7 +285,7 @@ private: Vector m_changes; - timespec m_fs_timestamp; + FsStatus m_fs_status; // Values are just data holding by the buffer, they are not part of its // observable state diff --git a/src/client.cc b/src/client.cc index c4312aca..d64d4120 100644 --- a/src/client.cc +++ b/src/client.cc @@ -287,7 +287,7 @@ void Client::reload_buffer() { context().print_status({ format("error while reloading buffer: '{}'", error.what()), context().faces()["Error"] }); - buffer.set_fs_timestamp(get_fs_timestamp(buffer.name())); + buffer.set_fs_status(get_fs_status(buffer.name())); } } @@ -312,7 +312,7 @@ void Client::on_buffer_reload_key(Key key) else if (key == 'n' or key == 'N' or key == Key::Escape) { // reread timestamp in case the file was modified again - buffer.set_fs_timestamp(get_fs_timestamp(buffer.name())); + buffer.set_fs_status(get_fs_status(buffer.name())); print_status({ format("'{}' kept", buffer.display_name()), context().faces()["Information"] }); if (key == 'N') @@ -354,9 +354,16 @@ void Client::check_if_buffer_needs_reloading() return; const String& filename = buffer.name(); - timespec ts = get_fs_timestamp(filename); - if (ts == InvalidTime or ts == buffer.fs_timestamp()) + const timespec ts = get_fs_timestamp(filename); + const auto status = buffer.fs_status(); + + if (ts == InvalidTime or ts == status.timestamp) return; + + if (MappedFile fd{filename}; + fd.st.st_size == status.file_size and hash_data(fd.data, fd.st.st_size) == status.hash) + return; + if (reload == Autoreload::Ask) { StringView bufname = buffer.display_name(); diff --git a/src/file.cc b/src/file.cc index c1f5bb26..e086cec7 100644 --- a/src/file.cc +++ b/src/file.cc @@ -610,6 +610,13 @@ timespec get_fs_timestamp(StringView filename) return st.st_mtim; } +FsStatus get_fs_status(StringView filename) +{ + MappedFile fd{filename}; + + return {fd.st.st_mtim, fd.st.st_size, hash_data(fd.data, fd.st.st_size)}; +} + String get_kak_binary_path() { char buffer[2048]; diff --git a/src/file.hh b/src/file.hh index a0e4e855..aa32b01d 100644 --- a/src/file.hh +++ b/src/file.hh @@ -86,7 +86,15 @@ Vector list_files(StringView directory); void make_directory(StringView dir, mode_t mode); +struct FsStatus +{ + timespec timestamp; + ByteCount file_size; + size_t hash; +}; + timespec get_fs_timestamp(StringView filename); +FsStatus get_fs_status(StringView filename); constexpr bool operator==(const timespec& lhs, const timespec& rhs) {