Detect file external modification and ask the user whether to reload or not
* Buffer now store a m_fs_timestamp field. * Client in Normal mode checks current buffer file every 500 ms, or each time it goes back to Normal mode.
This commit is contained in:
parent
fe0a4f0d11
commit
03c74b7a88
|
@ -708,4 +708,16 @@ char Buffer::byte_at(BufferCoord c) const
|
|||
return m_lines[c.line].content[c.column];
|
||||
}
|
||||
|
||||
time_t Buffer::fs_timestamp() const
|
||||
{
|
||||
kak_assert(m_flags & Flags::File);
|
||||
return m_fs_timestamp;
|
||||
}
|
||||
|
||||
void Buffer::set_fs_timestamp(time_t ts)
|
||||
{
|
||||
kak_assert(m_flags & Flags::File);
|
||||
m_fs_timestamp = ts;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -104,6 +104,8 @@ public:
|
|||
BufferIterator erase(BufferIterator begin, BufferIterator end);
|
||||
|
||||
size_t timestamp() const { return m_timestamp; }
|
||||
time_t fs_timestamp() const;
|
||||
void set_fs_timestamp(time_t ts);
|
||||
|
||||
void commit_undo_group();
|
||||
bool undo();
|
||||
|
@ -197,6 +199,8 @@ private:
|
|||
size_t m_last_save_undo_index;
|
||||
size_t m_timestamp;
|
||||
|
||||
time_t m_fs_timestamp;
|
||||
|
||||
// this is mutable as adding or removing listeners is not muting the
|
||||
// buffer observable state.
|
||||
mutable std::unordered_set<BufferChangeListener*> m_change_listeners;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "window.hh"
|
||||
#include "file.hh"
|
||||
#include "remote.hh"
|
||||
#include "client_manager.hh"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -45,6 +46,7 @@ namespace InputModes
|
|||
{
|
||||
|
||||
static constexpr std::chrono::milliseconds idle_timeout{100};
|
||||
static constexpr std::chrono::milliseconds fs_check_timeout{500};
|
||||
|
||||
class Normal : public InputMode
|
||||
{
|
||||
|
@ -53,8 +55,13 @@ public:
|
|||
: InputMode(client),
|
||||
m_idle_timer{Clock::now() + idle_timeout, [this](Timer& timer) {
|
||||
context().hooks().run_hook("NormalIdle", "", context());
|
||||
}},
|
||||
m_fs_check_timer{Clock::now() + fs_check_timeout, [this](Timer& timer) {
|
||||
context().client().check_buffer_fs_timestamp();
|
||||
timer.set_next_date(Clock::now() + fs_check_timeout);
|
||||
}}
|
||||
{
|
||||
context().client().check_buffer_fs_timestamp();
|
||||
context().hooks().run_hook("NormalBegin", "", context());
|
||||
}
|
||||
|
||||
|
@ -87,6 +94,7 @@ public:
|
|||
private:
|
||||
int m_count = 0;
|
||||
Timer m_idle_timer;
|
||||
Timer m_fs_check_timer;
|
||||
};
|
||||
|
||||
class LineEditor
|
||||
|
@ -1095,4 +1103,44 @@ void Client::reset_normal_mode()
|
|||
change_input_mode(new InputModes::Normal(*this));
|
||||
}
|
||||
|
||||
void Client::check_buffer_fs_timestamp()
|
||||
{
|
||||
Buffer& buffer = m_context.buffer();
|
||||
const String& filename = buffer.name();
|
||||
if (not (buffer.flags() & Buffer::Flags::File))
|
||||
return;
|
||||
time_t ts = get_fs_timestamp(filename);
|
||||
if (ts != buffer.fs_timestamp())
|
||||
{
|
||||
print_status({"'" + buffer.display_name() + "' was modified externally, press r or y to reload, k or n to keep", get_color("Prompt")});
|
||||
on_next_key([this, ts, filename](Key key, Context& context) {
|
||||
Buffer* buf = BufferManager::instance().get_buffer_ifp(filename);
|
||||
// buffer got deleted while waiting for the key, do nothing
|
||||
if (not buf)
|
||||
{
|
||||
print_status({});
|
||||
return;
|
||||
}
|
||||
if (key == 'r' or key == 'y')
|
||||
{
|
||||
DisplayCoord view_pos = context.window().position();
|
||||
BufferCoord cursor_pos = context.editor().main_selection().last();
|
||||
buf = create_buffer_from_file(filename);
|
||||
Window& win = ClientManager::instance().get_unused_window_for_buffer(*buf);
|
||||
win.select(cursor_pos);
|
||||
win.set_position(view_pos);
|
||||
context.change_editor(win);
|
||||
print_status({"'" + buf->display_name() + "' reloaded", get_color("Information") });
|
||||
}
|
||||
if (key == 'k' or key == 'n')
|
||||
{
|
||||
buf->set_fs_timestamp(ts);
|
||||
print_status({"'" + buf->display_name() + "' kept", get_color("Information") });
|
||||
}
|
||||
else
|
||||
check_buffer_fs_timestamp();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -86,6 +86,8 @@ public:
|
|||
|
||||
UserInterface& ui() const { return *m_ui; }
|
||||
|
||||
void check_buffer_fs_timestamp();
|
||||
|
||||
void reset_normal_mode();
|
||||
private:
|
||||
void change_input_mode(InputMode* new_mode);
|
||||
|
|
13
src/file.cc
13
src/file.cc
|
@ -176,6 +176,7 @@ Buffer* create_buffer_from_file(String filename)
|
|||
pos = line_end + 1;
|
||||
}
|
||||
Buffer* buffer = new Buffer(filename, Buffer::Flags::File, std::move(lines));
|
||||
buffer->set_fs_timestamp(st.st_mtime);
|
||||
|
||||
OptionManager& options = buffer->options();
|
||||
options.get_local_option("eolformat").set<String>(crlf ? "crlf" : "lf");
|
||||
|
@ -200,7 +201,7 @@ static void write(int fd, memoryview<char> data, const String& filename)
|
|||
}
|
||||
}
|
||||
|
||||
void write_buffer_to_file(const Buffer& buffer, const String& filename)
|
||||
void write_buffer_to_file(Buffer& buffer, const String& filename)
|
||||
{
|
||||
String eolformat = buffer.options()["eolformat"].get<String>();
|
||||
if (eolformat == "crlf")
|
||||
|
@ -226,6 +227,8 @@ void write_buffer_to_file(const Buffer& buffer, const String& filename)
|
|||
write(fd, linedata.subrange(0, linedata.size()-1), filename);
|
||||
write(fd, eoldata, filename);
|
||||
}
|
||||
if ((buffer.flags() & Buffer::Flags::File) and filename == buffer.name())
|
||||
buffer.set_fs_timestamp(get_fs_timestamp(filename));
|
||||
}
|
||||
|
||||
String find_file(const String& filename, memoryview<String> paths)
|
||||
|
@ -305,4 +308,12 @@ std::vector<String> complete_filename(const String& prefix,
|
|||
return real_result;
|
||||
}
|
||||
|
||||
time_t get_fs_timestamp(const String& filename)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(filename.c_str(), &st) != 0)
|
||||
throw runtime_error("stat failed on " + filename);
|
||||
return st.st_mtime;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,9 +30,11 @@ String compact_path(const String& filename);
|
|||
|
||||
String read_file(const String& filename);
|
||||
Buffer* create_buffer_from_file(String filename);
|
||||
void write_buffer_to_file(const Buffer& buffer, const String& filename);
|
||||
void write_buffer_to_file(Buffer& buffer, const String& filename);
|
||||
String find_file(const String& filename, memoryview<String> paths);
|
||||
|
||||
time_t get_fs_timestamp(const String& filename);
|
||||
|
||||
std::vector<String> complete_filename(const String& prefix,
|
||||
const Regex& ignore_regex,
|
||||
ByteCount cursor_pos = -1);
|
||||
|
|
Loading…
Reference in New Issue
Block a user