1 #include "buffer_manager.hh"
2 
3 #include "assert.hh"
4 #include "buffer.hh"
5 #include "client_manager.hh"
6 #include "exception.hh"
7 #include "file.hh"
8 #include "ranges.hh"
9 #include "string.hh"
10 
11 namespace Kakoune
12 {
13 
~BufferManager()14 BufferManager::~BufferManager()
15 {
16     // Move buffers to avoid running BufClose with buffers remaining in that list
17     BufferList buffers = std::move(m_buffers);
18 
19     for (auto& buffer : buffers)
20         buffer->on_unregistered();
21 
22     // Make sure not clients exists
23     if (ClientManager::has_instance())
24         ClientManager::instance().clear(true);
25 }
26 
create_buffer(String name,Buffer::Flags flags,BufferLines lines,ByteOrderMark bom,EolFormat eolformat,FsStatus fs_status)27 Buffer* BufferManager::create_buffer(String name, Buffer::Flags flags, BufferLines lines, ByteOrderMark bom, EolFormat eolformat, FsStatus fs_status)
28 {
29     auto path = real_path(parse_filename(name));
30     for (auto& buf : m_buffers)
31     {
32         if (buf->name() == name or
33             (buf->flags() & Buffer::Flags::File and buf->name() == path))
34             throw runtime_error{"buffer name is already in use"};
35     }
36 
37     m_buffers.push_back(std::make_unique<Buffer>(std::move(name), flags, lines, bom, eolformat, fs_status));
38     auto* buffer = m_buffers.back().get();
39     buffer->on_registered();
40 
41     if (contains(m_buffer_trash, buffer))
42         throw runtime_error{"buffer got removed during its creation"};
43 
44     return buffer;
45 }
46 
delete_buffer(Buffer & buffer)47 void BufferManager::delete_buffer(Buffer& buffer)
48 {
49     auto it = find_if(m_buffers, [&](auto& p) { return p.get() == &buffer; });
50     if (it == m_buffers.end()) // we might be trying to recursively delete this buffer
51         return;
52 
53     m_buffer_trash.emplace_back(std::move(*it));
54     m_buffers.erase(it);
55 
56     if (ClientManager::has_instance())
57         ClientManager::instance().ensure_no_client_uses_buffer(buffer);
58 
59     buffer.on_unregistered();
60 }
61 
get_buffer_ifp(StringView name)62 Buffer* BufferManager::get_buffer_ifp(StringView name)
63 {
64     auto path = real_path(parse_filename(name));
65     for (auto& buf : m_buffers)
66     {
67         if (buf->name() == name or
68             (buf->flags() & Buffer::Flags::File and buf->name() == path))
69             return buf.get();
70     }
71     return nullptr;
72 }
73 
get_buffer(StringView name)74 Buffer& BufferManager::get_buffer(StringView name)
75 {
76     Buffer* res = get_buffer_ifp(name);
77     if (not res)
78         throw runtime_error{format("no such buffer '{}'", name)};
79     return *res;
80 }
81 
get_first_buffer()82 Buffer& BufferManager::get_first_buffer()
83 {
84     if (all_of(m_buffers, [](auto& b) { return (b->flags() & Buffer::Flags::Debug); }))
85         create_buffer("*scratch*", Buffer::Flags::None,
86                       {StringData::create({"*** this is a *scratch* buffer which won't be automatically saved ***\n"}),
87                        StringData::create({"*** use it for notes or open a file buffer with the :edit command ***\n"})},
88                       ByteOrderMark::None, EolFormat::Lf, {InvalidTime, {}, {}});
89 
90     return *m_buffers.back();
91 }
92 
backup_modified_buffers()93 void BufferManager::backup_modified_buffers()
94 {
95     for (auto& buf : m_buffers)
96     {
97         if ((buf->flags() & Buffer::Flags::File) and buf->is_modified()
98             and not (buf->flags() & Buffer::Flags::ReadOnly))
99             write_buffer_to_backup_file(*buf);
100     }
101 }
102 
clear_buffer_trash()103 void BufferManager::clear_buffer_trash()
104 {
105     m_buffer_trash.clear();
106 }
107 
arrange_buffers(ConstArrayView<String> first_ones)108 void BufferManager::arrange_buffers(ConstArrayView<String> first_ones)
109 {
110     Vector<size_t> indices;
111     for (const auto& name : first_ones)
112     {
113         auto it = find_if(m_buffers, [&](auto& buf) { return buf->name() == name or buf->display_name() == name; });
114         if (it == m_buffers.end())
115             throw runtime_error{format("no such buffer '{}'", name)};
116         size_t index = it - m_buffers.begin();
117         if (contains(indices, index))
118             throw runtime_error{format("buffer '{}' appears more than once", name)};
119         indices.push_back(index);
120     }
121 
122     BufferList res;
123     for (size_t index : indices)
124         res.push_back(std::move(m_buffers[index]));
125     for (auto& buf : m_buffers)
126     {
127         if (buf)
128             res.push_back(std::move(buf));
129     }
130     m_buffers = std::move(res);
131 }
132 
133 }
134