Store timespec for buffer fs timestamps, not just time_t
time_t has a resolution of one second, which cause troubles when a file changes multiple time during that same second.
This commit is contained in:
parent
122a799ecb
commit
e2720f1fbe
|
@ -18,7 +18,7 @@ namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
Buffer::Buffer(String name, Flags flags, BufferLines lines,
|
Buffer::Buffer(String name, Flags flags, BufferLines lines,
|
||||||
time_t fs_timestamp)
|
timespec fs_timestamp)
|
||||||
: Scope(GlobalScope::instance()),
|
: Scope(GlobalScope::instance()),
|
||||||
m_name((flags & Flags::File) ? real_path(parse_filename(name)) : std::move(name)),
|
m_name((flags & Flags::File) ? real_path(parse_filename(name)) : std::move(name)),
|
||||||
m_display_name((flags & Flags::File) ? compact_path(m_name) : m_name),
|
m_display_name((flags & Flags::File) ? compact_path(m_name) : m_name),
|
||||||
|
@ -160,7 +160,7 @@ struct Buffer::Modification
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void Buffer::reload(BufferLines lines, time_t fs_timestamp)
|
void Buffer::reload(BufferLines lines, timespec fs_timestamp)
|
||||||
{
|
{
|
||||||
if (lines.empty())
|
if (lines.empty())
|
||||||
lines.emplace_back(StringData::create("\n"));
|
lines.emplace_back(StringData::create("\n"));
|
||||||
|
@ -531,13 +531,13 @@ ByteCoord Buffer::char_prev(ByteCoord coord) const
|
||||||
return coord;
|
return coord;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t Buffer::fs_timestamp() const
|
timespec Buffer::fs_timestamp() const
|
||||||
{
|
{
|
||||||
kak_assert(m_flags & Flags::File);
|
kak_assert(m_flags & Flags::File);
|
||||||
return m_fs_timestamp;
|
return m_fs_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Buffer::set_fs_timestamp(time_t ts)
|
void Buffer::set_fs_timestamp(timespec ts)
|
||||||
{
|
{
|
||||||
kak_assert(m_flags & Flags::File);
|
kak_assert(m_flags & Flags::File);
|
||||||
m_fs_timestamp = ts;
|
m_fs_timestamp = ts;
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Kakoune
|
||||||
|
|
||||||
class Buffer;
|
class Buffer;
|
||||||
|
|
||||||
constexpr time_t InvalidTime = 0;
|
constexpr timespec InvalidTime = { -1, -1 };
|
||||||
|
|
||||||
// A BufferIterator permits to iterate over the characters of a buffer
|
// A BufferIterator permits to iterate over the characters of a buffer
|
||||||
class BufferIterator
|
class BufferIterator
|
||||||
|
@ -79,7 +79,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
Buffer(String name, Flags flags, BufferLines lines = {},
|
Buffer(String name, Flags flags, BufferLines lines = {},
|
||||||
time_t fs_timestamp = InvalidTime);
|
timespec fs_timestamp = InvalidTime);
|
||||||
Buffer(const Buffer&) = delete;
|
Buffer(const Buffer&) = delete;
|
||||||
Buffer& operator= (const Buffer&) = delete;
|
Buffer& operator= (const Buffer&) = delete;
|
||||||
~Buffer();
|
~Buffer();
|
||||||
|
@ -93,8 +93,8 @@ public:
|
||||||
BufferIterator erase(BufferIterator begin, BufferIterator end);
|
BufferIterator erase(BufferIterator begin, BufferIterator end);
|
||||||
|
|
||||||
size_t timestamp() const;
|
size_t timestamp() const;
|
||||||
time_t fs_timestamp() const;
|
timespec fs_timestamp() const;
|
||||||
void set_fs_timestamp(time_t ts);
|
void set_fs_timestamp(timespec ts);
|
||||||
|
|
||||||
void commit_undo_group();
|
void commit_undo_group();
|
||||||
bool undo();
|
bool undo();
|
||||||
|
@ -152,7 +152,7 @@ public:
|
||||||
|
|
||||||
void run_hook_in_own_context(StringView hook_name, StringView param);
|
void run_hook_in_own_context(StringView hook_name, StringView param);
|
||||||
|
|
||||||
void reload(BufferLines lines, time_t fs_timestamp = InvalidTime);
|
void reload(BufferLines lines, timespec fs_timestamp = InvalidTime);
|
||||||
|
|
||||||
void check_invariant() const;
|
void check_invariant() const;
|
||||||
|
|
||||||
|
@ -213,7 +213,7 @@ private:
|
||||||
|
|
||||||
Vector<Change, MemoryDomain::BufferMeta> m_changes;
|
Vector<Change, MemoryDomain::BufferMeta> m_changes;
|
||||||
|
|
||||||
time_t m_fs_timestamp;
|
timespec m_fs_timestamp;
|
||||||
|
|
||||||
// Values are just data holding by the buffer, they are not part of its
|
// Values are just data holding by the buffer, they are not part of its
|
||||||
// observable state
|
// observable state
|
||||||
|
|
|
@ -47,7 +47,7 @@ ByteCount get_byte_to_column(const Buffer& buffer, CharCount tabstop, CharCoord
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer* create_buffer_from_data(StringView data, StringView name,
|
Buffer* create_buffer_from_data(StringView data, StringView name,
|
||||||
Buffer::Flags flags, time_t fs_timestamp)
|
Buffer::Flags flags, timespec fs_timestamp)
|
||||||
{
|
{
|
||||||
bool bom = false, crlf = false;
|
bool bom = false, crlf = false;
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ Buffer* create_fifo_buffer(String name, int fd, bool scroll)
|
||||||
if (buffer)
|
if (buffer)
|
||||||
{
|
{
|
||||||
buffer->flags() |= Buffer::Flags::NoUndo;
|
buffer->flags() |= Buffer::Flags::NoUndo;
|
||||||
buffer->reload({"\n"_ss}, 0);
|
buffer->reload({"\n"_ss}, InvalidTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
buffer = new Buffer(std::move(name), Buffer::Flags::Fifo | Buffer::Flags::NoUndo);
|
buffer = new Buffer(std::move(name), Buffer::Flags::Fifo | Buffer::Flags::NoUndo);
|
||||||
|
|
|
@ -34,7 +34,7 @@ Buffer* create_fifo_buffer(String name, int fd, bool scroll = false);
|
||||||
|
|
||||||
Buffer* create_buffer_from_data(StringView data, StringView name,
|
Buffer* create_buffer_from_data(StringView data, StringView name,
|
||||||
Buffer::Flags flags,
|
Buffer::Flags flags,
|
||||||
time_t fs_timestamp = InvalidTime);
|
timespec fs_timestamp = InvalidTime);
|
||||||
|
|
||||||
void write_to_debug_buffer(StringView str);
|
void write_to_debug_buffer(StringView str);
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ void Client::check_if_buffer_needs_reloading()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const String& filename = buffer.name();
|
const String& filename = buffer.name();
|
||||||
time_t ts = get_fs_timestamp(filename);
|
timespec ts = get_fs_timestamp(filename);
|
||||||
if (ts == InvalidTime or ts == buffer.fs_timestamp())
|
if (ts == InvalidTime or ts == buffer.fs_timestamp())
|
||||||
return;
|
return;
|
||||||
if (reload == Ask)
|
if (reload == Ask)
|
||||||
|
|
16
src/file.cc
16
src/file.cc
|
@ -180,13 +180,13 @@ Buffer* create_buffer_from_file(StringView filename)
|
||||||
throw file_access_error(real_filename, "is a directory");
|
throw file_access_error(real_filename, "is a directory");
|
||||||
if (S_ISFIFO(st.st_mode)) // Do not try to read fifos, use them as write only
|
if (S_ISFIFO(st.st_mode)) // Do not try to read fifos, use them as write only
|
||||||
return create_buffer_from_data({}, real_filename,
|
return create_buffer_from_data({}, real_filename,
|
||||||
Buffer::Flags::File, st.st_mtime);
|
Buffer::Flags::File, st.st_mtim);
|
||||||
|
|
||||||
const char* data = (const char*)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
const char* data = (const char*)mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||||
auto unmap = on_scope_end([&]{ munmap((void*)data, st.st_size); });
|
auto unmap = on_scope_end([&]{ munmap((void*)data, st.st_size); });
|
||||||
|
|
||||||
return create_buffer_from_data({data, data + st.st_size}, real_filename,
|
return create_buffer_from_data({data, data + st.st_size}, real_filename,
|
||||||
Buffer::Flags::File, st.st_mtime);
|
Buffer::Flags::File, st.st_mtim);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void write(int fd, StringView data)
|
static void write(int fd, StringView data)
|
||||||
|
@ -423,11 +423,11 @@ Vector<String> complete_command(StringView prefix, ByteCount cursor_pos)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef decltype(stat::st_mtime) TimeSpec;
|
typedef decltype(stat::st_mtim) TimeSpec;
|
||||||
|
|
||||||
struct CommandCache
|
struct CommandCache
|
||||||
{
|
{
|
||||||
TimeSpec mtime = {};
|
TimeSpec mtim = {};
|
||||||
Vector<String> commands;
|
Vector<String> commands;
|
||||||
};
|
};
|
||||||
static UnorderedMap<String, CommandCache, MemoryDomain::Commands> command_cache;
|
static UnorderedMap<String, CommandCache, MemoryDomain::Commands> command_cache;
|
||||||
|
@ -442,7 +442,7 @@ Vector<String> complete_command(StringView prefix, ByteCount cursor_pos)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto& cache = command_cache[dirname];
|
auto& cache = command_cache[dirname];
|
||||||
if (memcmp(&cache.mtime, &st.st_mtime, sizeof(TimeSpec)) != 0)
|
if (memcmp(&cache.mtim, &st.st_mtim, sizeof(TimeSpec)) != 0)
|
||||||
{
|
{
|
||||||
auto filter = [&](const dirent& entry) {
|
auto filter = [&](const dirent& entry) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -457,7 +457,7 @@ Vector<String> complete_command(StringView prefix, ByteCount cursor_pos)
|
||||||
};
|
};
|
||||||
|
|
||||||
cache.commands = list_files("", dirname, filter);
|
cache.commands = list_files("", dirname, filter);
|
||||||
memcpy(&cache.mtime, &st.st_mtime, sizeof(TimeSpec));
|
memcpy(&cache.mtim, &st.st_mtim, sizeof(TimeSpec));
|
||||||
}
|
}
|
||||||
for (auto& cmd : cache.commands)
|
for (auto& cmd : cache.commands)
|
||||||
{
|
{
|
||||||
|
@ -471,12 +471,12 @@ Vector<String> complete_command(StringView prefix, ByteCount cursor_pos)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t get_fs_timestamp(StringView filename)
|
timespec get_fs_timestamp(StringView filename)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(filename.zstr(), &st) != 0)
|
if (stat(filename.zstr(), &st) != 0)
|
||||||
return InvalidTime;
|
return InvalidTime;
|
||||||
return st.st_mtime;
|
return st.st_mtim;
|
||||||
}
|
}
|
||||||
|
|
||||||
String get_kak_binary_path()
|
String get_kak_binary_path()
|
||||||
|
|
12
src/file.hh
12
src/file.hh
|
@ -52,7 +52,17 @@ Vector<String> list_files(StringView directory);
|
||||||
|
|
||||||
void make_directory(StringView dir);
|
void make_directory(StringView dir);
|
||||||
|
|
||||||
time_t get_fs_timestamp(StringView filename);
|
timespec get_fs_timestamp(StringView filename);
|
||||||
|
|
||||||
|
constexpr bool operator==(const timespec& lhs, const timespec& rhs)
|
||||||
|
{
|
||||||
|
return lhs.tv_sec == rhs.tv_sec and lhs.tv_nsec == rhs.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator!=(const timespec& lhs, const timespec& rhs)
|
||||||
|
{
|
||||||
|
return not (lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
CandidateList complete_filename(StringView prefix, const Regex& ignore_regex,
|
CandidateList complete_filename(StringView prefix, const Regex& ignore_regex,
|
||||||
ByteCount cursor_pos = -1);
|
ByteCount cursor_pos = -1);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user