2011-09-02 20:51:20 +04:00
|
|
|
#include "buffer.hh"
|
2011-09-08 04:11:48 +04:00
|
|
|
|
|
|
|
#include "buffer_manager.hh"
|
2011-09-08 04:13:19 +04:00
|
|
|
#include "window.hh"
|
2011-09-09 23:24:18 +04:00
|
|
|
#include "assert.hh"
|
2011-09-17 18:13:33 +04:00
|
|
|
#include "utils.hh"
|
2012-04-03 16:01:01 +04:00
|
|
|
#include "hook_manager.hh"
|
2012-01-23 17:56:43 +04:00
|
|
|
#include "context.hh"
|
2011-09-08 04:11:48 +04:00
|
|
|
|
2012-03-06 18:27:03 +04:00
|
|
|
#include <algorithm>
|
|
|
|
|
2011-09-02 20:51:20 +04:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
T clamp(T min, T max, T val)
|
|
|
|
{
|
|
|
|
if (val < min)
|
|
|
|
return min;
|
|
|
|
if (val > max)
|
|
|
|
return max;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2011-10-07 18:15:55 +04:00
|
|
|
Buffer::Buffer(const std::string& name, Type type,
|
2012-03-09 01:23:29 +04:00
|
|
|
const String& initial_content)
|
2011-10-07 18:15:55 +04:00
|
|
|
: m_name(name), m_type(type),
|
|
|
|
m_history(1), m_history_cursor(m_history.begin()),
|
2012-03-13 01:31:27 +04:00
|
|
|
m_last_save_undo_index(0)
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2011-09-08 04:11:48 +04:00
|
|
|
BufferManager::instance().register_buffer(this);
|
2012-03-13 01:31:27 +04:00
|
|
|
if (not initial_content.empty())
|
|
|
|
apply_modification(Modification::make_insert(begin(), initial_content));
|
2011-09-08 04:11:48 +04:00
|
|
|
|
2012-01-31 18:01:48 +04:00
|
|
|
if (type == Type::NewFile)
|
2012-04-03 16:01:01 +04:00
|
|
|
GlobalHookManager::instance().run_hook("BufCreate", name, Context(*this));
|
2012-01-31 18:01:48 +04:00
|
|
|
else if (type == Type::File)
|
2012-04-03 16:01:01 +04:00
|
|
|
GlobalHookManager::instance().run_hook("BufOpen", name, Context(*this));
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2011-10-24 18:23:13 +04:00
|
|
|
Buffer::~Buffer()
|
|
|
|
{
|
|
|
|
m_windows.clear();
|
2012-03-26 18:21:49 +04:00
|
|
|
BufferManager::instance().unregister_buffer(this);
|
2011-10-24 18:23:13 +04:00
|
|
|
assert(m_modification_listeners.empty());
|
|
|
|
}
|
|
|
|
|
2011-09-05 23:06:31 +04:00
|
|
|
BufferIterator Buffer::iterator_at(const BufferCoord& line_and_column) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
return BufferIterator(*this, clamp(line_and_column));
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2011-09-05 23:06:31 +04:00
|
|
|
BufferCoord Buffer::line_and_column_at(const BufferIterator& iterator) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
return iterator.m_coord;
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
BufferPos Buffer::line_at(const BufferIterator& iterator) const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
return iterator.line();
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
BufferSize Buffer::line_length(BufferPos line) const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
assert(line < line_count());
|
2012-02-27 23:52:36 +04:00
|
|
|
BufferPos end = (line < m_lines.size() - 1) ?
|
2012-03-30 15:37:18 +04:00
|
|
|
m_lines[line + 1].start : length();
|
|
|
|
return end - m_lines[line].start;
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2011-09-05 23:06:31 +04:00
|
|
|
BufferCoord Buffer::clamp(const BufferCoord& line_and_column) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
|
|
|
if (m_lines.empty())
|
2011-09-05 23:06:31 +04:00
|
|
|
return BufferCoord();
|
2011-09-02 20:51:20 +04:00
|
|
|
|
2011-09-05 23:06:31 +04:00
|
|
|
BufferCoord result(line_and_column.line, line_and_column.column);
|
2012-03-06 18:27:03 +04:00
|
|
|
result.line = Kakoune::clamp<int>(0, m_lines.size() - 1, result.line);
|
2012-03-30 15:37:18 +04:00
|
|
|
int max_col = std::max(0, line_length(result.line) - 2);
|
2011-11-24 18:23:41 +04:00
|
|
|
result.column = Kakoune::clamp<int>(0, max_col, result.column);
|
2011-09-02 20:51:20 +04:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2011-11-28 23:31:29 +04:00
|
|
|
BufferIterator Buffer::iterator_at_line_begin(const BufferIterator& iterator) const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
return BufferIterator(*this, { iterator.line(), 0 });
|
2011-11-28 23:31:29 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
BufferIterator Buffer::iterator_at_line_end(const BufferIterator& iterator) const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
BufferPos line = iterator.line();
|
|
|
|
return ++BufferIterator(*this, { line, std::max(line_length(line) - 1, 0) });
|
2011-11-28 23:31:29 +04:00
|
|
|
}
|
|
|
|
|
2011-09-02 20:51:20 +04:00
|
|
|
BufferIterator Buffer::begin() const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
return BufferIterator(*this, { 0, 0 });
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
BufferIterator Buffer::end() const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (m_lines.empty())
|
|
|
|
return BufferIterator(*this, { 0, 0 });
|
2012-03-30 16:00:40 +04:00
|
|
|
return BufferIterator(*this, { (int)line_count()-1, (int)m_lines.back().length() });
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
BufferSize Buffer::length() const
|
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (m_lines.empty())
|
|
|
|
return 0;
|
2012-03-30 16:00:40 +04:00
|
|
|
return m_lines.back().start + m_lines.back().length();
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2011-09-22 17:58:35 +04:00
|
|
|
BufferSize Buffer::line_count() const
|
|
|
|
{
|
2012-03-06 18:27:03 +04:00
|
|
|
return m_lines.size();
|
2011-09-22 17:58:35 +04:00
|
|
|
}
|
|
|
|
|
2012-03-09 01:23:29 +04:00
|
|
|
String Buffer::string(const BufferIterator& begin, const BufferIterator& end) const
|
2011-09-02 20:51:20 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
String res;
|
|
|
|
for (BufferPos line = begin.line(); line <= end.line(); ++line)
|
|
|
|
{
|
|
|
|
size_t start = 0;
|
|
|
|
if (line == begin.line())
|
|
|
|
start = begin.column();
|
|
|
|
size_t count = -1;
|
|
|
|
if (line == end.line())
|
|
|
|
count = end.column() - start;
|
|
|
|
res += m_lines[line].content.substr(start, count);
|
|
|
|
}
|
|
|
|
return res;
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|
|
|
|
|
2011-09-06 22:49:32 +04:00
|
|
|
void Buffer::begin_undo_group()
|
|
|
|
{
|
|
|
|
assert(m_current_undo_group.empty());
|
|
|
|
m_history.erase(m_history_cursor, m_history.end());
|
2011-11-03 17:44:02 +04:00
|
|
|
|
|
|
|
if (m_history.size() < m_last_save_undo_index)
|
|
|
|
m_last_save_undo_index = -1;
|
|
|
|
|
2011-09-06 22:49:32 +04:00
|
|
|
m_history_cursor = m_history.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::end_undo_group()
|
|
|
|
{
|
|
|
|
m_history.push_back(m_current_undo_group);
|
|
|
|
m_history_cursor = m_history.end();
|
|
|
|
|
|
|
|
m_current_undo_group.clear();
|
|
|
|
}
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
Modification Modification::inverse() const
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2011-10-17 18:12:15 +04:00
|
|
|
Type inverse_type;
|
2011-09-06 22:49:32 +04:00
|
|
|
switch (type)
|
|
|
|
{
|
2011-10-17 18:12:15 +04:00
|
|
|
case Insert: inverse_type = Erase; break;
|
|
|
|
case Erase: inverse_type = Insert; break;
|
2011-09-06 22:49:32 +04:00
|
|
|
default: assert(false);
|
|
|
|
}
|
2011-12-06 22:58:43 +04:00
|
|
|
return Modification(inverse_type, position, content);
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Buffer::undo()
|
|
|
|
{
|
|
|
|
if (m_history_cursor == m_history.begin())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
--m_history_cursor;
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
for (const Modification& modification : reversed(*m_history_cursor))
|
2011-10-18 04:55:45 +04:00
|
|
|
apply_modification(modification.inverse());
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Buffer::redo()
|
|
|
|
{
|
|
|
|
if (m_history_cursor == m_history.end())
|
|
|
|
return false;
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
for (const Modification& modification : *m_history_cursor)
|
2011-10-18 04:55:45 +04:00
|
|
|
apply_modification(modification);
|
2011-09-06 22:49:32 +04:00
|
|
|
|
|
|
|
++m_history_cursor;
|
|
|
|
}
|
|
|
|
|
2012-03-30 15:37:18 +04:00
|
|
|
void Buffer::check_invariant() const
|
|
|
|
{
|
|
|
|
BufferSize start = 0;
|
|
|
|
for (auto& line : m_lines)
|
|
|
|
{
|
|
|
|
assert(line.start == start);
|
2012-03-30 16:00:40 +04:00
|
|
|
start += line.length();
|
2012-03-30 15:37:18 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::insert(const BufferIterator& pos, const String& content)
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
BufferSize offset = pos.offset();
|
2012-03-06 18:27:03 +04:00
|
|
|
|
2012-03-30 15:37:18 +04:00
|
|
|
// all following lines advanced by length
|
|
|
|
for (size_t i = pos.line()+1; i < line_count(); ++i)
|
|
|
|
m_lines[i].start += content.length();
|
2012-02-23 01:54:25 +04:00
|
|
|
|
2012-03-30 15:37:18 +04:00
|
|
|
// if we inserted at the end of the buffer, we may have created a new
|
|
|
|
// line without inserting a '\n'
|
|
|
|
if (pos == end() and (pos == begin() or *(pos-1) == '\n'))
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
int start = 0;
|
|
|
|
for (int i = 0; i < content.length(); ++i)
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (content[i] == '\n')
|
|
|
|
{
|
|
|
|
m_lines.push_back({ offset + start, content.substr(start, i + 1 - start) });
|
|
|
|
start = i + 1;
|
|
|
|
}
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
if (start != content.length())
|
|
|
|
m_lines.push_back({ offset + start, content.substr(start) });
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
else
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
String prefix = m_lines[pos.line()].content.substr(0, pos.column());
|
|
|
|
String suffix = m_lines[pos.line()].content.substr(pos.column());
|
|
|
|
|
|
|
|
auto line_it = m_lines.begin() + pos.line();
|
|
|
|
line_it = m_lines.erase(line_it);
|
2012-02-23 01:54:25 +04:00
|
|
|
|
2012-03-30 15:37:18 +04:00
|
|
|
int start = 0;
|
|
|
|
for (int i = 0; i < content.length(); ++i)
|
2012-02-23 01:54:25 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
if (content[i] == '\n')
|
|
|
|
{
|
|
|
|
String line_content = content.substr(start, i + 1 - start);
|
|
|
|
if (start == 0)
|
|
|
|
{
|
|
|
|
line_content = prefix + line_content;
|
|
|
|
line_it = m_lines.insert(line_it, { offset + start - (int)prefix.length(),
|
|
|
|
std::move(line_content) });
|
|
|
|
}
|
|
|
|
else
|
|
|
|
line_it = m_lines.insert(line_it, { offset + start,
|
|
|
|
std::move(line_content) });
|
|
|
|
|
|
|
|
++line_it;
|
|
|
|
start = i + 1;
|
|
|
|
}
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
if (start == 0)
|
|
|
|
m_lines.insert(line_it, { offset + start - (int)prefix.length(), prefix + content + suffix });
|
|
|
|
else
|
|
|
|
m_lines.insert(line_it, { offset + start, content.substr(start) + suffix });
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
|
|
|
|
check_invariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::erase(const BufferIterator& pos, BufferSize length)
|
|
|
|
{
|
|
|
|
BufferIterator end = pos + length;
|
|
|
|
String prefix = m_lines[pos.line()].content.substr(0, pos.column());
|
|
|
|
String suffix = m_lines[end.line()].content.substr(end.column());
|
|
|
|
Line new_line = { m_lines[pos.line()].start, prefix + suffix };
|
|
|
|
|
|
|
|
m_lines.erase(m_lines.begin() + pos.line(), m_lines.begin() + end.line() + 1);
|
|
|
|
m_lines.insert(m_lines.begin() + pos.line(), new_line);
|
|
|
|
|
|
|
|
for (size_t i = pos.line()+1; i < line_count(); ++i)
|
|
|
|
m_lines[i].start -= length;
|
|
|
|
|
|
|
|
check_invariant();
|
2012-02-23 01:54:25 +04:00
|
|
|
}
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
void Buffer::apply_modification(const Modification& modification)
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2012-03-30 15:37:18 +04:00
|
|
|
const String& content = modification.content;
|
|
|
|
const BufferIterator& pos = modification.position;
|
|
|
|
|
2011-09-06 22:49:32 +04:00
|
|
|
switch (modification.type)
|
|
|
|
{
|
2011-12-06 22:58:43 +04:00
|
|
|
case Modification::Insert:
|
2012-03-30 15:37:18 +04:00
|
|
|
insert(modification.position, modification.content);
|
2011-09-06 22:49:32 +04:00
|
|
|
break;
|
2011-12-06 22:58:43 +04:00
|
|
|
case Modification::Erase:
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2011-11-27 22:41:25 +04:00
|
|
|
size_t size = modification.content.size();
|
|
|
|
assert(string(modification.position, modification.position + size)
|
|
|
|
== modification.content);
|
2012-03-30 15:37:18 +04:00
|
|
|
erase(modification.position, size);
|
2011-09-06 22:49:32 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
2012-03-30 15:37:18 +04:00
|
|
|
|
2011-10-18 04:55:45 +04:00
|
|
|
for (auto listener : m_modification_listeners)
|
|
|
|
listener->on_modification(modification);
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
2011-12-07 18:26:40 +04:00
|
|
|
void Buffer::modify(Modification&& modification)
|
2011-09-06 22:49:32 +04:00
|
|
|
{
|
2012-03-13 01:31:27 +04:00
|
|
|
if (modification.content.empty())
|
|
|
|
return;
|
|
|
|
|
2011-10-18 04:55:45 +04:00
|
|
|
apply_modification(modification);
|
2011-10-17 18:12:15 +04:00
|
|
|
m_current_undo_group.push_back(std::move(modification));
|
2011-09-06 22:49:32 +04:00
|
|
|
}
|
|
|
|
|
2011-09-08 18:30:36 +04:00
|
|
|
Window* Buffer::get_or_create_window()
|
2011-09-08 04:13:19 +04:00
|
|
|
{
|
2011-09-08 18:30:36 +04:00
|
|
|
if (m_windows.empty())
|
|
|
|
m_windows.push_front(std::unique_ptr<Window>(new Window(*this)));
|
2011-09-08 04:13:19 +04:00
|
|
|
|
2011-09-08 18:30:36 +04:00
|
|
|
return m_windows.front().get();
|
2011-09-08 04:13:19 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::delete_window(Window* window)
|
|
|
|
{
|
|
|
|
assert(&window->buffer() == this);
|
|
|
|
auto window_it = std::find(m_windows.begin(), m_windows.end(), window);
|
|
|
|
assert(window_it != m_windows.end());
|
|
|
|
m_windows.erase(window_it);
|
|
|
|
}
|
|
|
|
|
2011-10-05 18:21:24 +04:00
|
|
|
bool Buffer::is_modified() const
|
|
|
|
{
|
2011-11-03 17:44:02 +04:00
|
|
|
size_t history_cursor_index = m_history_cursor - m_history.begin();
|
|
|
|
return m_last_save_undo_index != history_cursor_index
|
2011-10-05 18:21:24 +04:00
|
|
|
or not m_current_undo_group.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Buffer::notify_saved()
|
|
|
|
{
|
2011-11-03 17:44:02 +04:00
|
|
|
size_t history_cursor_index = m_history_cursor - m_history.begin();
|
|
|
|
m_last_save_undo_index = history_cursor_index;
|
2011-10-05 18:21:24 +04:00
|
|
|
}
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
void Buffer::register_modification_listener(ModificationListener* listener)
|
2011-10-18 04:55:45 +04:00
|
|
|
{
|
|
|
|
assert(listener);
|
|
|
|
assert(not contains(m_modification_listeners, listener));
|
|
|
|
m_modification_listeners.push_back(listener);
|
|
|
|
}
|
|
|
|
|
2011-12-06 22:58:43 +04:00
|
|
|
void Buffer::unregister_modification_listener(ModificationListener* listener)
|
2011-10-18 04:55:45 +04:00
|
|
|
{
|
|
|
|
assert(listener);
|
|
|
|
auto it = std::find(m_modification_listeners.begin(),
|
|
|
|
m_modification_listeners.end(),
|
|
|
|
listener);
|
|
|
|
assert(it != m_modification_listeners.end());
|
|
|
|
m_modification_listeners.erase(it);
|
|
|
|
}
|
|
|
|
|
2011-09-02 20:51:20 +04:00
|
|
|
}
|