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];
|
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);
|
BufferIterator erase(BufferIterator begin, BufferIterator end);
|
||||||
|
|
||||||
size_t timestamp() const { return m_timestamp; }
|
size_t timestamp() const { return m_timestamp; }
|
||||||
|
time_t fs_timestamp() const;
|
||||||
|
void set_fs_timestamp(time_t ts);
|
||||||
|
|
||||||
void commit_undo_group();
|
void commit_undo_group();
|
||||||
bool undo();
|
bool undo();
|
||||||
|
@ -197,6 +199,8 @@ private:
|
||||||
size_t m_last_save_undo_index;
|
size_t m_last_save_undo_index;
|
||||||
size_t m_timestamp;
|
size_t m_timestamp;
|
||||||
|
|
||||||
|
time_t m_fs_timestamp;
|
||||||
|
|
||||||
// this is mutable as adding or removing listeners is not muting the
|
// this is mutable as adding or removing listeners is not muting the
|
||||||
// buffer observable state.
|
// buffer observable state.
|
||||||
mutable std::unordered_set<BufferChangeListener*> m_change_listeners;
|
mutable std::unordered_set<BufferChangeListener*> m_change_listeners;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "window.hh"
|
#include "window.hh"
|
||||||
#include "file.hh"
|
#include "file.hh"
|
||||||
#include "remote.hh"
|
#include "remote.hh"
|
||||||
|
#include "client_manager.hh"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ namespace InputModes
|
||||||
{
|
{
|
||||||
|
|
||||||
static constexpr std::chrono::milliseconds idle_timeout{100};
|
static constexpr std::chrono::milliseconds idle_timeout{100};
|
||||||
|
static constexpr std::chrono::milliseconds fs_check_timeout{500};
|
||||||
|
|
||||||
class Normal : public InputMode
|
class Normal : public InputMode
|
||||||
{
|
{
|
||||||
|
@ -53,8 +55,13 @@ public:
|
||||||
: InputMode(client),
|
: InputMode(client),
|
||||||
m_idle_timer{Clock::now() + idle_timeout, [this](Timer& timer) {
|
m_idle_timer{Clock::now() + idle_timeout, [this](Timer& timer) {
|
||||||
context().hooks().run_hook("NormalIdle", "", context());
|
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());
|
context().hooks().run_hook("NormalBegin", "", context());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +94,7 @@ public:
|
||||||
private:
|
private:
|
||||||
int m_count = 0;
|
int m_count = 0;
|
||||||
Timer m_idle_timer;
|
Timer m_idle_timer;
|
||||||
|
Timer m_fs_check_timer;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LineEditor
|
class LineEditor
|
||||||
|
@ -1095,4 +1103,44 @@ void Client::reset_normal_mode()
|
||||||
change_input_mode(new InputModes::Normal(*this));
|
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; }
|
UserInterface& ui() const { return *m_ui; }
|
||||||
|
|
||||||
|
void check_buffer_fs_timestamp();
|
||||||
|
|
||||||
void reset_normal_mode();
|
void reset_normal_mode();
|
||||||
private:
|
private:
|
||||||
void change_input_mode(InputMode* new_mode);
|
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;
|
pos = line_end + 1;
|
||||||
}
|
}
|
||||||
Buffer* buffer = new Buffer(filename, Buffer::Flags::File, std::move(lines));
|
Buffer* buffer = new Buffer(filename, Buffer::Flags::File, std::move(lines));
|
||||||
|
buffer->set_fs_timestamp(st.st_mtime);
|
||||||
|
|
||||||
OptionManager& options = buffer->options();
|
OptionManager& options = buffer->options();
|
||||||
options.get_local_option("eolformat").set<String>(crlf ? "crlf" : "lf");
|
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>();
|
String eolformat = buffer.options()["eolformat"].get<String>();
|
||||||
if (eolformat == "crlf")
|
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, linedata.subrange(0, linedata.size()-1), filename);
|
||||||
write(fd, eoldata, 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)
|
String find_file(const String& filename, memoryview<String> paths)
|
||||||
|
@ -305,4 +308,12 @@ std::vector<String> complete_filename(const String& prefix,
|
||||||
return real_result;
|
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);
|
String read_file(const String& filename);
|
||||||
Buffer* create_buffer_from_file(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);
|
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,
|
std::vector<String> complete_filename(const String& prefix,
|
||||||
const Regex& ignore_regex,
|
const Regex& ignore_regex,
|
||||||
ByteCount cursor_pos = -1);
|
ByteCount cursor_pos = -1);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user