home/src/buffer_manager.cc
Maxime Coste 56e2174cf6 Do not reuse m_buffer_trash to store dying buffers in ~BufferManager
Doing that clears m_buffer_trash, deleting buffers contained there,
but we are not ready to have Buffer destructors running yet as we
did not clear the ClientManager, meaning we might have free windows
pointing to buffers in the buffer trash.
2018-05-21 20:30:24 +10:00

119 lines
3.1 KiB
C++

#include "buffer_manager.hh"
#include "assert.hh"
#include "buffer.hh"
#include "client_manager.hh"
#include "exception.hh"
#include "file.hh"
#include "ranges.hh"
#include "string.hh"
namespace Kakoune
{
BufferManager::~BufferManager()
{
// Move buffers to avoid running BufClose with buffers remaining in that list
BufferList buffers = std::move(m_buffers);
for (auto& buffer : buffers)
buffer->on_unregistered();
// Make sure not clients exists
if (ClientManager::has_instance())
ClientManager::instance().clear();
}
Buffer* BufferManager::create_buffer(String name, Buffer::Flags flags,
StringView data, timespec fs_timestamp)
{
auto path = real_path(parse_filename(name));
for (auto& buf : m_buffers)
{
if (buf->name() == name or
(buf->flags() & Buffer::Flags::File and buf->name() == path))
throw runtime_error{"buffer name is already in use"};
}
m_buffers.push_back(std::make_unique<Buffer>(std::move(name), flags, data, fs_timestamp));
auto* buffer = m_buffers.back().get();
buffer->on_registered();
if (contains(m_buffer_trash, buffer))
throw runtime_error{"buffer got removed during its creation"};
return buffer;
}
void BufferManager::delete_buffer(Buffer& buffer)
{
auto it = find_if(m_buffers, [&](auto& p) { return p.get() == &buffer; });
kak_assert(it != m_buffers.end());
m_buffer_trash.emplace_back(std::move(*it));
m_buffers.erase(it);
if (ClientManager::has_instance())
ClientManager::instance().ensure_no_client_uses_buffer(buffer);
buffer.on_unregistered();
}
Buffer* BufferManager::get_buffer_ifp(StringView name)
{
auto path = real_path(parse_filename(name));
for (auto& buf : m_buffers)
{
if (buf->name() == name or
(buf->flags() & Buffer::Flags::File and buf->name() == path))
return buf.get();
}
return nullptr;
}
Buffer& BufferManager::get_buffer(StringView name)
{
Buffer* res = get_buffer_ifp(name);
if (not res)
throw runtime_error{format("no such buffer '{}'", name)};
return *res;
}
Buffer& BufferManager::get_first_buffer()
{
if (all_of(m_buffers, [](auto& b) { return (b->flags() & Buffer::Flags::Debug); }))
create_buffer("*scratch*", Buffer::Flags::None);
return *m_buffers.back();
}
void BufferManager::backup_modified_buffers()
{
for (auto& buf : m_buffers)
{
if ((buf->flags() & Buffer::Flags::File) and buf->is_modified()
and not (buf->flags() & Buffer::Flags::ReadOnly))
write_buffer_to_backup_file(*buf);
}
}
void BufferManager::clear_buffer_trash()
{
for (auto& buffer : m_buffer_trash)
{
// Do that again, to be tolerant in some corner cases, where a buffer is
// deleted during its creation
if (ClientManager::has_instance())
{
ClientManager::instance().ensure_no_client_uses_buffer(*buffer);
ClientManager::instance().clear_window_trash();
}
buffer.reset();
}
m_buffer_trash.clear();
}
}